Skip to main content

hotline_rs/
imgui.rs

1use imgui_sys::*;
2
3use crate::gfx::Heap;
4use crate::os;
5use crate::os::App;
6use crate::os::Window;
7use crate::os::NativeHandle;
8
9use crate::gfx;
10use crate::gfx::Buffer;
11use crate::gfx::CmdBuf;
12use crate::gfx::Device;
13use crate::gfx::SwapChain;
14use crate::gfx::Texture;
15
16use std::ffi::CStr;
17use std::ffi::CString;
18
19use maths_rs::Vec4f;
20
21use std::ptr::addr_of;
22use std::ptr::addr_of_mut;
23
24macro_rules! static_ref_mut{
25    ($place:expr) => {
26        &mut *addr_of_mut!($place)
27    }
28}
29
30fn to_im_vec4(v: Vec4f) -> ImVec4 {
31    unsafe {
32        std::mem::transmute(v)
33    }
34}
35
36const DEFAULT_VB_SIZE: i32 = 5000;
37const DEFAULT_IB_SIZE: i32 = 10000;
38const MAX_RANGES: usize = 32;
39
40const MAIN_DOCKSPACE_FLAGS : u32 = ImGuiWindowFlags_NoTitleBar |
41    ImGuiWindowFlags_NoCollapse |
42    ImGuiWindowFlags_NoResize |
43    ImGuiWindowFlags_NoMove |
44    ImGuiWindowFlags_NoBringToFrontOnFocus |
45    ImGuiWindowFlags_NoNavFocus |
46    ImGuiWindowFlags_MenuBar |
47    ImGuiWindowFlags_NoScrollbar;
48
49const IMVEC2_ZERO : ImVec2 = ImVec2 {x: 0.0, y: 0.0 };
50const MAIN_DOCK_NAME : *const i8 = "main_dock\0".as_ptr() as *const i8;
51const STATUS_BAR_NAME : *const i8 = "status_bar\0".as_ptr() as *const i8;
52
53/// Info to supply fonts from .ttf files for use with imgui
54pub struct FontInfo {
55    /// filepath to a .ttf file
56    pub filepath: String,
57    /// optional to specify ranges, which are wide char u32 code points. defaults to basic latin & extended latin
58    pub glyph_ranges: Option<Vec<[u32; 2]>>
59}
60
61/// Info required to create an instance of imgui
62pub struct ImGuiInfo<'stack, D: Device, A: App> {
63    pub device: &'stack mut D,
64    pub swap_chain: &'stack mut D::SwapChain,
65    pub main_window: &'stack A::Window,
66    pub fonts: Vec<FontInfo>,
67}
68
69/// The concrete `ImGui` instance itself
70pub struct ImGui<D: Device, A: App> {
71    _native_handle: A::NativeHandle,
72    _font_texture: D::Texture,
73    pipeline: D::RenderPipeline,
74    buffers: Vec<RenderBuffers<D>>,
75    last_cursor: os::Cursor
76}
77
78#[derive(Clone)]
79struct RenderBuffers<D: Device> {
80    vb: D::Buffer,
81    ib: D::Buffer,
82    vb_size: i32,
83    ib_size: i32,
84}
85
86struct ViewportData<D: Device, A: App> {
87    /// if viewport is main, we get the window from UserData and the rest of this struct is null
88    main_viewport: bool,
89    window: Vec<A::Window>,
90    swap_chain: Vec<D::SwapChain>,
91    cmd: Vec<D::CmdBuf>,
92    buffers: Vec<RenderBuffers<D>>,
93}
94
95struct UserData<'a, D: Device, A: App> {
96    app: &'a mut A,
97    device: &'a mut D,
98    main_window: &'a mut A::Window,
99    pipeline: &'a D::RenderPipeline,
100    image_heaps: &'a Vec<&'a D::Heap>
101}
102
103/// Trait for hooking into imgui ui calls into other modules
104pub trait UserInterface<D: gfx::Device, A: os::App> {
105    fn show_ui(&mut self, imgui: &mut ImGui<D, A>, open: bool) -> bool;
106}
107
108bitflags! {
109    pub struct WindowFlags : i32 {
110        const NONE = 0;
111        const NO_TITLE_BAR = 1 << 0;
112        const NO_RESIZE = 1 << 1;
113        const NO_MOVE = 1 << 2;
114        const NO_SCROLLBAR = 1 << 3;
115        const NO_SCROLL_WITH_MOUSE = 1 << 4;
116        const NO_COLLAPSE = 1 << 5;
117        const ALWAYS_AUTO_RESIZE = 1 << 6;
118        const NO_BACKGROUND = 1 << 7;
119        const NO_SAVED_SETTINGS = 1 << 8;
120        const NO_MOUSE_INPUTS = 1 << 9;
121        const MENU_BAR = 1 << 10;
122        const HORIZONTAL_SCROLLBAR = 1 << 11;
123        const NO_FOCUS_ON_APPEARING = 1 << 12;
124        const NO_BRING_TO_FRONT_ON_FOCUS = 1 << 13;
125        const ALWAYS_VERTICAL_SCROLLBAR = 1 << 14;
126        const ALWAYS_HORIZONTAL_SCROLLBAR = 1 << 15;
127        const ALWAYS_USE_WINDOW_PADDING = 1 << 16;
128        const NO_NAV_INPUTS = 1 << 18;
129        const NO_NAV_FOCUS = 1 << 19;
130        const UNSAVED_DOCUMENT = 1 << 20;
131        const NO_DOCKING = 1 << 21;
132        const NO_NAV = (1 << 18 | 1 << 19);
133        const NO_DECORATION = (1 << 0 | 1 << 1 | 1 << 3 | 1 << 5);
134        const NO_INPUTS = (1 << 9 | 1 << 18 | 1 << 19);
135    }
136}
137
138impl From<WindowFlags> for i32 {
139    fn from(mask: WindowFlags) -> i32 {
140        mask.bits
141    }
142}
143
144fn new_viewport_data<D: Device, A: App>() -> *mut ViewportData<D, A> {
145    unsafe {
146        let layout =
147            std::alloc::Layout::from_size_align(std::mem::size_of::<ViewportData<D, A>>(), 8).unwrap();
148        std::alloc::alloc_zeroed(layout) as *mut ViewportData<D, A>
149    }
150}
151
152fn new_native_handle<A: App>(handle: A::NativeHandle) -> *mut A::NativeHandle {
153    unsafe {
154        let layout = std::alloc::Layout::from_size_align(
155            std::mem::size_of::<A::NativeHandle>(),
156            8,
157        )
158        .unwrap();
159        let nh = std::alloc::alloc_zeroed(layout) as *mut A::NativeHandle;
160        *nh = handle;
161        nh
162    }
163}
164
165fn new_monitors(monitors: &Vec<ImGuiPlatformMonitor>) -> *mut ImGuiPlatformMonitor {
166    unsafe {
167        let size_bytes = std::mem::size_of::<ImGuiPlatformMonitor>() * monitors.len();
168        let layout = std::alloc::Layout::from_size_align(size_bytes, 8).unwrap();
169        let ptr = std::alloc::alloc_zeroed(layout) as *mut ImGuiPlatformMonitor;
170        std::ptr::copy_nonoverlapping(monitors.as_ptr(), ptr, monitors.len());
171        ptr
172    }
173}
174
175fn new_ranges() -> *mut [u32; (MAX_RANGES * 2) + 1] {
176    unsafe {
177        let layout = std::alloc::Layout::from_size_align(std::mem::size_of::<[u32; (MAX_RANGES * 2) + 1]>(), 8).unwrap();
178        std::alloc::alloc_zeroed(layout) as *mut [u32; (MAX_RANGES * 2) + 1]
179    }
180}
181
182fn to_imgui_texture_id<D: Device>(tex: &D::Texture) -> ImTextureID {
183    unsafe {
184        let srv = tex.get_srv_index().unwrap() as u64;
185        let heap_id = tex.get_shader_heap_id().unwrap() as u64;
186
187        // packs them in so the first 48bits are for srv
188        // top 16 bits are heap id
189
190        // mask off 48 bits 0x
191        let mask = 0x0000ffffffffffff;
192        let combined = (srv & mask) | ((heap_id << 48) & !mask);
193        let tex_id : *mut cty::c_void = std::ptr::null_mut();
194        tex_id.add(combined as usize)
195    }
196}
197
198fn to_srv_heap_id(tex_id: *mut cty::c_void) -> (usize, u16) {
199    let mask = 0x0000ffffffffffff;
200    let srv_id = (tex_id as u64) & mask;
201    let heap_id = ((tex_id as u64) & !mask) >> 48;
202    (srv_id as usize, heap_id as u16)
203}
204
205fn create_fonts_texture<D: Device>(
206    device: &mut D,
207) -> Result<D::Texture, super::Error> {
208    unsafe {
209        let io = &*igGetIO();
210        let mut out_pixels: *mut u8 = std::ptr::null_mut();
211        let mut out_width = 0;
212        let mut out_height = 0;
213        let mut out_bytes_per_pixel = 0;
214        ImFontAtlas_GetTexDataAsRGBA32(
215            io.Fonts,
216            &mut out_pixels,
217            &mut out_width,
218            &mut out_height,
219            &mut out_bytes_per_pixel,
220        );
221
222        let data_size = out_bytes_per_pixel * out_width * out_height;
223        let data_slice = std::slice::from_raw_parts(out_pixels, data_size as usize);
224
225        let tex_info = gfx::TextureInfo {
226            format: gfx::Format::RGBA8n,
227            tex_type: gfx::TextureType::Texture2D,
228            width: out_width as u64,
229            height: out_height as u64,
230            depth: 1,
231            array_layers: 1,
232            mip_levels: 1,
233            samples: 1,
234            usage: gfx::TextureUsage::SHADER_RESOURCE,
235            initial_state: gfx::ResourceState::ShaderResource,
236        };
237
238        device.create_texture(&tex_info, Some(data_slice))
239    }
240}
241
242fn create_render_pipeline<D: Device, A: App>(info: &ImGuiInfo<D, A>) -> Result<D::RenderPipeline, super::Error> {
243    let device = &info.device;
244    let swap_chain = &info.swap_chain;
245
246    // TODO: temp: compile shaders
247    let src = "
248        cbuffer vertexBuffer : register(b0)
249        {
250            float4x4 ProjectionMatrix;
251        };
252        struct VS_INPUT
253        {
254            float2 pos : POSITION;
255            float4 col : COLOR0;
256            float2 uv  : TEXCOORD0;
257        };
258        
259        struct PS_INPUT
260        {
261            float4 pos : SV_POSITION;
262            float4 col : COLOR0;
263            float2 uv  : TEXCOORD0;
264        };
265        
266        PS_INPUT VSMain(VS_INPUT input)
267        {
268            PS_INPUT output;
269            output.pos = mul(ProjectionMatrix, float4(input.pos.xy, 0.0, 1.0));
270            output.col = input.col;
271            output.uv  = input.uv;
272            return output;
273        }
274
275        SamplerState sampler0 : register(s0);
276        Texture2D texture0 : register(t0);
277        
278        float4 PSMain(PS_INPUT input) : SV_Target
279        {
280            float4 out_col = input.col * texture0.Sample(sampler0, input.uv);
281            return out_col;
282        }";
283
284    let vs_info = gfx::ShaderInfo {
285        shader_type: gfx::ShaderType::Vertex,
286        compile_info: Some(gfx::ShaderCompileInfo {
287            entry_point: String::from("VSMain"),
288            target: String::from("vs_5_0"),
289            flags: gfx::ShaderCompileFlags::NONE,
290        }),
291    };
292
293    let fs_info = gfx::ShaderInfo {
294        shader_type: gfx::ShaderType::Fragment,
295        compile_info: Some(gfx::ShaderCompileInfo {
296            entry_point: String::from("PSMain"),
297            target: String::from("ps_5_0"),
298            flags: gfx::ShaderCompileFlags::NONE,
299        }),
300    };
301
302    let vs = device.create_shader(&vs_info, src.as_bytes())?;
303    let fs = device.create_shader(&fs_info, src.as_bytes())?;
304
305    device.create_render_pipeline(&gfx::RenderPipelineInfo {
306        vs: Some(&vs),
307        fs: Some(&fs),
308        input_layout: vec![
309            gfx::InputElementInfo {
310                semantic: String::from("POSITION"),
311                index: 0,
312                format: gfx::Format::RG32f,
313                input_slot: 0,
314                aligned_byte_offset: 0,
315                input_slot_class: gfx::InputSlotClass::PerVertex,
316                step_rate: 0,
317            },
318            gfx::InputElementInfo {
319                semantic: String::from("TEXCOORD"),
320                index: 0,
321                format: gfx::Format::RG32f,
322                input_slot: 0,
323                aligned_byte_offset: 8,
324                input_slot_class: gfx::InputSlotClass::PerVertex,
325                step_rate: 0,
326            },
327            gfx::InputElementInfo {
328                semantic: String::from("COLOR"),
329                index: 0,
330                format: gfx::Format::RGBA8n,
331                input_slot: 0,
332                aligned_byte_offset: 16,
333                input_slot_class: gfx::InputSlotClass::PerVertex,
334                step_rate: 0,
335            },
336        ],
337        pipeline_layout: gfx::PipelineLayout {
338            push_constants: Some(vec![gfx::PushConstantInfo {
339                visibility: gfx::ShaderVisibility::Vertex,
340                num_values: 16,
341                shader_register: 0,
342                register_space: 0,
343            }]),
344            bindings: Some(vec![gfx::DescriptorBinding {
345                visibility: gfx::ShaderVisibility::Fragment,
346                binding_type: gfx::DescriptorType::ShaderResource,
347                num_descriptors: Some(1),
348                shader_register: 0,
349                register_space: 0,
350            }]),
351            static_samplers: Some(vec![gfx::SamplerBinding {
352                visibility: gfx::ShaderVisibility::Fragment,
353                shader_register: 0,
354                register_space: 0,
355                sampler_info: gfx::SamplerInfo {
356                filter: gfx::SamplerFilter::Linear,
357                address_u: gfx::SamplerAddressMode::Wrap,
358                address_v: gfx::SamplerAddressMode::Wrap,
359                address_w: gfx::SamplerAddressMode::Wrap,
360                comparison: None,
361                border_colour: None,
362                mip_lod_bias: 0.0,
363                max_aniso: 0,
364                min_lod: -1.0,
365                max_lod: -1.0,
366            }}]),
367        },
368        blend_info: gfx::BlendInfo {
369            independent_blend_enabled: true,
370            render_target: vec![gfx::RenderTargetBlendInfo {
371                blend_enabled: true,
372                logic_op_enabled: false,
373                src_blend: gfx::BlendFactor::SrcAlpha,
374                dst_blend: gfx::BlendFactor::InvSrcAlpha,
375                blend_op: gfx::BlendOp::Add,
376                src_blend_alpha: gfx::BlendFactor::One,
377                dst_blend_alpha: gfx::BlendFactor::InvSrcAlpha,
378                blend_op_alpha: gfx::BlendOp::Add,
379                logic_op: gfx::LogicOp::Clear,
380                write_mask: gfx::WriteMask::ALL,
381            }],
382            ..Default::default()
383        },
384        topology: gfx::Topology::TriangleList,
385        pass: Some(swap_chain.get_backbuffer_pass()),
386        ..Default::default()
387    })
388}
389
390fn create_vertex_buffer<D: Device>(
391    device: &mut D,
392    size: i32,
393) -> Result<D::Buffer, super::Error> {
394    device.create_buffer::<u8>(
395        &gfx::BufferInfo {
396            usage: gfx::BufferUsage::VERTEX,
397            cpu_access: gfx::CpuAccessFlags::WRITE,
398            format: gfx::Format::Unknown,
399            stride: std::mem::size_of::<ImDrawVert>(),
400            num_elements: size as usize,
401            initial_state: gfx::ResourceState::VertexConstantBuffer
402        },
403        None,
404    )
405}
406
407fn create_index_buffer<D: Device>(
408    device: &mut D,
409    size: i32,
410) -> Result<D::Buffer, super::Error> {
411    device.create_buffer::<u8>(
412        &gfx::BufferInfo {
413            usage: gfx::BufferUsage::INDEX,
414            cpu_access: gfx::CpuAccessFlags::WRITE,
415            format: gfx::Format::R16u,
416            stride: std::mem::size_of::<ImDrawIdx>(),
417            num_elements: size as usize,
418            initial_state: gfx::ResourceState::IndexBuffer
419        },
420        None,
421    )
422}
423
424fn render_draw_data<D: Device>(
425    draw_data: &ImDrawData,
426    device: &mut D,
427    cmd: &mut D::CmdBuf,
428    image_heaps: &Vec<&D::Heap>,
429    buffers: &mut [RenderBuffers<D>],
430    pipeline: &D::RenderPipeline,
431) -> Result<(), super::Error> where D::RenderPipeline: gfx::Pipeline {
432    unsafe {
433        if draw_data.CmdListsCount > 0 && draw_data.TotalVtxCount > 0 {
434            let bb = cmd.get_backbuffer_index() as usize;
435
436            let buffers = &mut buffers[bb];
437
438            // resize vb
439            if draw_data.TotalVtxCount > buffers.vb_size {
440                buffers.vb = create_vertex_buffer::<D>(device, draw_data.TotalVtxCount)?;
441                buffers.vb_size = draw_data.TotalVtxCount;
442            }
443
444            // resize ib
445            if draw_data.TotalIdxCount > buffers.ib_size {
446                buffers.ib = create_index_buffer::<D>(device, draw_data.TotalIdxCount)?;
447                buffers.ib_size = draw_data.TotalIdxCount;
448            }
449
450            // update buffers
451            let imgui_cmd_lists =
452                std::slice::from_raw_parts(draw_data.CmdLists, draw_data.CmdListsCount as usize);
453            let mut vertex_write_offset = 0;
454            let mut index_write_offset = 0;
455
456            for imgui_cmd_list in imgui_cmd_lists {
457                // vertex
458                let draw_vert = &(*(*imgui_cmd_list)).VtxBuffer;
459                let vb_size_bytes = draw_vert.Size as usize * std::mem::size_of::<ImDrawVert>();
460                let vb_slice = std::slice::from_raw_parts(draw_vert.Data, draw_vert.Size as usize);
461                buffers.vb.update(vertex_write_offset, vb_slice)?;
462                vertex_write_offset += vb_size_bytes;
463                // index
464                let draw_index = &(*(*imgui_cmd_list)).IdxBuffer;
465                let ib_size_bytes = draw_index.Size as usize * std::mem::size_of::<ImDrawIdx>();
466                let ib_slice = std::slice::from_raw_parts(draw_index.Data, draw_index.Size as usize);
467                buffers.ib.update(index_write_offset, ib_slice)?;
468                index_write_offset += ib_size_bytes;
469            }
470
471            // update push constants
472            let l = draw_data.DisplayPos.x;
473            let r = draw_data.DisplayPos.x + draw_data.DisplaySize.x;
474            let t = draw_data.DisplayPos.y;
475            let b = draw_data.DisplayPos.y + draw_data.DisplaySize.y;
476
477            let mvp: [[f32; 4]; 4] = [
478                [2.0 / (r - l), 0.0, 0.0, 0.0],
479                [0.0, 2.0 / (t - b), 0.0, 0.0],
480                [0.0, 0.0, 0.5, 0.0],
481                [(r + l) / (l - r), (t + b) / (b - t), 0.0, 1.0],
482            ];
483
484            let viewport = gfx::Viewport {
485                x: 0.0,
486                y: 0.0,
487                width: draw_data.DisplaySize.x,
488                height: draw_data.DisplaySize.y,
489                min_depth: 0.0,
490                max_depth: 1.0,
491            };
492
493            cmd.begin_event(0xff1fb6c4, "imgui");
494            cmd.set_viewport(&viewport);
495            cmd.set_vertex_buffer(&buffers.vb, 0);
496            cmd.set_index_buffer(&buffers.ib);
497            cmd.set_render_pipeline(pipeline);
498            cmd.push_render_constants(0, 16, 0, &mvp);
499
500            let clip_off = draw_data.DisplayPos;
501            let mut global_vtx_offset = 0;
502            let mut global_idx_offset = 0;
503            for imgui_cmd_list in imgui_cmd_lists {
504                let imgui_cmd_buffer = (**imgui_cmd_list).CmdBuffer;
505                let imgui_cmd_data =
506                    std::slice::from_raw_parts(imgui_cmd_buffer.Data, imgui_cmd_buffer.Size as usize);
507                let draw_vert = &(*(*imgui_cmd_list)).VtxBuffer;
508                let draw_index = &(*(*imgui_cmd_list)).IdxBuffer;
509                for cmd_data in imgui_cmd_data.iter().take(imgui_cmd_buffer.Size as usize) {
510                    let imgui_cmd = &cmd_data;
511                    if imgui_cmd.UserCallback.is_some() {
512                        // TODO:
513                    } 
514                    else {
515                        let clip_min_x = imgui_cmd.ClipRect.x - clip_off.x;
516                        let clip_min_y = imgui_cmd.ClipRect.y - clip_off.y;
517                        let clip_max_x = imgui_cmd.ClipRect.z - clip_off.x;
518                        let clip_max_y = imgui_cmd.ClipRect.w - clip_off.y;
519                        if clip_max_x < clip_min_x || clip_max_y < clip_min_y {
520                            continue;
521                        }
522
523                        let scissor = gfx::ScissorRect {
524                            left: clip_min_x as i32,
525                            top: clip_min_y as i32,
526                            right: clip_max_x as i32,
527                            bottom: clip_max_y as i32,
528                        };
529
530                        let (srv, heap_id) = to_srv_heap_id(imgui_cmd.TextureId);
531                        if heap_id == device.get_shader_heap().get_heap_id() {
532                            // bind the device heap
533                            cmd.set_binding(pipeline, device.get_shader_heap(), 1, srv);
534                        }
535                        else {
536                            // bund srv in another heap
537                            for heap in image_heaps {
538                                if heap.get_heap_id() == heap_id {
539                                    cmd.set_binding(pipeline, heap, 1, srv);
540                                    break;
541                                }
542                            }
543                        }
544
545                        cmd.set_scissor_rect(&scissor);
546                        cmd.draw_indexed_instanced(
547                            imgui_cmd.ElemCount,
548                            1,
549                            imgui_cmd.IdxOffset + global_idx_offset,
550                            (imgui_cmd.VtxOffset + global_vtx_offset) as i32,
551                            0,
552                        );
553                    }
554                }
555                global_idx_offset += draw_index.Size as u32;
556                global_vtx_offset += draw_vert.Size as u32;
557            }
558            cmd.end_event();
559        }
560        Ok(())
561    }
562}
563
564impl<D, A> ImGui<D, A> where D: Device, A: App, D::RenderPipeline: gfx::Pipeline {
565    /// Internal utility for styling hotline colours in a hardcoded fashion
566    fn style_colours_hotline() {
567        unsafe {
568            igStyleColorsDark(std::ptr::null_mut());
569
570            let style = &mut *igGetStyle();
571            style.Colors[ImGuiCol_WindowBg as usize].w = 1.0;
572            style.WindowRounding = 2.0;
573            style.TabRounding = 2.0;
574
575            let colors = &mut style.Colors;
576
577            let hl = [
578                // sand
579                ImVec4{x: 251.0/255.0, y: 211.0/255.0, z: 122.0/255.0, w: 1.0},
580                ImVec4{x: 191.0/255.0, y: 161.0/255.0, z: 93.0/255.0, w: 1.0},
581
582                // mauve
583                ImVec4{x: 179.0/255.0, y:  85.0/255.0, z: 149.0/255.0, w: 1.0},
584                ImVec4{x: 131.0/255.0, y:  61.0/255.0, z: 109.0/255.0, w: 1.0},
585                ImVec4{x:  93.0/255.0, y:  50.0/255.0, z:  79.0/255.0, w: 1.0},
586
587                // darker sand
588                ImVec4{x: 251.0/255.0, y: 180.0/255.0, z: 93.0/255.0, w: 1.0},
589            ];
590
591            let bg = [
592                // warm grey
593                ImVec4{x: 0.31, y: 0.305, z: 0.30, w: 1.0},
594                ImVec4{x: 0.21, y: 0.205, z: 0.21, w: 1.0},
595                ImVec4{x: 0.15, y: 0.150, z: 0.15, w: 1.0},
596                ImVec4{x: 0.11, y: 0.105, z: 0.10, w: 1.0},
597            ];
598
599            colors[ImGuiCol_WindowBg as usize] = bg[3];
600            colors[ImGuiCol_Header as usize] = hl[3];
601            colors[ImGuiCol_HeaderActive as usize] = hl[1];
602            colors[ImGuiCol_Button as usize] = hl[4];
603            colors[ImGuiCol_ButtonHovered as usize] = hl[1];
604            colors[ImGuiCol_ButtonActive as usize] = hl[0];
605            colors[ImGuiCol_FrameBg as usize] = bg[1];
606            colors[ImGuiCol_FrameBgHovered as usize] = bg[0];
607            colors[ImGuiCol_FrameBgActive as usize] = bg[2];
608            colors[ImGuiCol_Tab as usize] = hl[3];
609            colors[ImGuiCol_TabHovered as usize] = hl[5];
610            colors[ImGuiCol_TabActive as usize] = hl[1];
611            colors[ImGuiCol_TabUnfocused as usize] = bg[2];
612            colors[ImGuiCol_TabUnfocusedActive as usize] = hl[3];
613            colors[ImGuiCol_TitleBg as usize] = bg[2];
614            colors[ImGuiCol_TitleBgActive as usize] = hl[4];
615            colors[ImGuiCol_TitleBgCollapsed as usize] = bg[2];
616
617            //
618            colors[ImGuiCol_CheckMark as usize] = hl[1];
619            colors[ImGuiCol_SliderGrab as usize] = hl[1];
620            colors[ImGuiCol_SliderGrabActive as usize] = hl[0];
621            colors[ImGuiCol_HeaderHovered as usize] = hl[1];
622            colors[ImGuiCol_ResizeGrip as usize] = bg[0];
623            colors[ImGuiCol_ResizeGripActive as usize] = hl[0];
624            colors[ImGuiCol_ResizeGripHovered as usize] = hl[1];
625       }
626    }
627
628    /// Create a new `ImGui` instance from `ImGuiInfo`
629    pub fn create(info: &mut ImGuiInfo<D, A>) -> Result<Self, super::Error> {
630        unsafe {
631            igCreateContext(std::ptr::null_mut());
632            let io = &mut *igGetIO();
633
634            io.ConfigFlags |= ImGuiConfigFlags_DockingEnable as i32;
635            io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable as i32;
636
637            // construct path for ini to be along side the exe
638            let exe_path = std::env::current_exe().ok().unwrap();
639            if let Some(parent) = exe_path.parent() {
640                // create static here, for persistent pointer
641                let ini_file = parent.join("imgui.ini");
642                static mut NULL_INI_FILE : Option<CString> = None;
643                NULL_INI_FILE = Some(CString::new(ini_file.to_str().unwrap().to_string()).unwrap());
644                if let Some(i) = &*addr_of!(NULL_INI_FILE) {
645                    io.IniFilename = i.as_ptr() as _;
646                }
647            };
648        
649            Self::style_colours_hotline();
650
651            let style = &mut *igGetStyle();
652            style.WindowRounding = 0.0;
653            style.Colors[imgui_sys::ImGuiCol_WindowBg as usize].w = 1.0;
654
655            // add fonts
656            let mut merge = false;
657
658            let mut font_ranges = Vec::new();
659            let mut font_ranges_alloc = Vec::new();
660            let mut font_names = Vec::new();
661
662            for font in &info.fonts {
663                let names_back = font_names.len();
664                font_names.push(CString::new(font.filepath.clone()).unwrap());
665
666                let config = ImFontConfig_ImFontConfig();
667                (*config).MergeMode = merge;
668
669                // copy over the font ranges          
670                let null_term_ranges = new_ranges();
671                let mut itr = 0;
672                
673                if let Some(ranges) = &font.glyph_ranges {
674                    // we alloc a fixed sized array on the heap for imgui glyph ranges
675                    // if you have more ranges that size will need increasing
676                    assert!(ranges.len() < MAX_RANGES);
677                    for range in ranges {
678                        (*null_term_ranges)[itr] = range[0];
679                        (*null_term_ranges)[itr+1] = range[1];
680                        itr += 2;
681                    }
682                }
683                font_ranges.push(null_term_ranges);
684                
685                // pass ranges or null
686                let p_ranges = if font.glyph_ranges.is_some() {
687                    null_term_ranges as *mut u32
688                }
689                else {
690                    std::ptr::null_mut()
691                };
692                font_ranges_alloc.push(p_ranges);
693
694                ImFontAtlas_AddFontFromFileTTF(
695                    io.Fonts,
696                    font_names[names_back].as_ptr() as *const i8,
697                    16.0,
698                    config,
699                    p_ranges
700                );
701
702                // subsequent fonts are merged
703                merge = true;
704            }
705
706            io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard as i32;
707            io.KeyMap[ImGuiKey_Tab as usize] = A::get_key_code(os::Key::Tab);
708            io.KeyMap[ImGuiKey_LeftArrow as usize] = A::get_key_code(os::Key::Left);
709            io.KeyMap[ImGuiKey_RightArrow as usize] =
710                A::get_key_code(os::Key::Right);
711            io.KeyMap[ImGuiKey_UpArrow as usize] = A::get_key_code(os::Key::Up);
712            io.KeyMap[ImGuiKey_DownArrow as usize] = A::get_key_code(os::Key::Down);
713            io.KeyMap[ImGuiKey_PageUp as usize] = A::get_key_code(os::Key::PageUp);
714            io.KeyMap[ImGuiKey_PageDown as usize] =
715                A::get_key_code(os::Key::PageDown);
716            io.KeyMap[ImGuiKey_Home as usize] = A::get_key_code(os::Key::Home);
717            io.KeyMap[ImGuiKey_End as usize] = A::get_key_code(os::Key::End);
718            io.KeyMap[ImGuiKey_Insert as usize] = A::get_key_code(os::Key::Insert);
719            io.KeyMap[ImGuiKey_Delete as usize] = A::get_key_code(os::Key::Delete);
720            io.KeyMap[ImGuiKey_Backspace as usize] =
721                A::get_key_code(os::Key::Backspace);
722            io.KeyMap[ImGuiKey_Space as usize] = A::get_key_code(os::Key::Space);
723            io.KeyMap[ImGuiKey_Enter as usize] = A::get_key_code(os::Key::Enter);
724            io.KeyMap[ImGuiKey_Escape as usize] = A::get_key_code(os::Key::Escape);
725            io.KeyMap[ImGuiKey_KeyPadEnter as usize] =
726                A::get_key_code(os::Key::KeyPadEnter);
727            io.KeyMap[ImGuiKey_A as usize] = 'A' as i32;
728            io.KeyMap[ImGuiKey_C as usize] = 'C' as i32;
729            io.KeyMap[ImGuiKey_V as usize] = 'V' as i32;
730            io.KeyMap[ImGuiKey_X as usize] = 'X' as i32;
731            io.KeyMap[ImGuiKey_Y as usize] = 'Y' as i32;
732            io.KeyMap[ImGuiKey_Z as usize] = 'Z' as i32;
733
734            // io setup
735            io.BackendPlatformName = "imgui_impl_hotline".as_ptr() as *const i8;
736            io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors as i32;
737            io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos as i32;
738            io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports as i32;
739            io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport as i32;
740
741            // renderer setup
742            io.BackendRendererName = "imgui_impl_hotline".as_ptr() as *const i8;
743            io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset as i32;
744            io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports as i32;
745
746            // create render buffers
747            let mut buffers: Vec<RenderBuffers<D>> = Vec::new();
748            let num_buffers = (*info.swap_chain).get_num_buffers();
749
750            let font_tex = create_fonts_texture::<D>(info.device)?;
751
752            let font_tex_id = to_imgui_texture_id::<D>(&font_tex);
753            ImFontAtlas_SetTexID(io.Fonts, font_tex_id);
754
755            let pipeline = create_render_pipeline(info)?;
756
757            for _i in 0..num_buffers {
758                buffers.push(RenderBuffers {
759                    vb: create_vertex_buffer::<D>(info.device, DEFAULT_VB_SIZE)?,
760                    vb_size: DEFAULT_VB_SIZE,
761                    ib: create_index_buffer::<D>(info.device, DEFAULT_IB_SIZE)?,
762                    ib_size: DEFAULT_IB_SIZE,
763                })
764            }
765
766            // enum monitors
767            let mut monitors: Vec<ImGuiPlatformMonitor> = Vec::new();
768
769            let platform_io = &mut *igGetPlatformIO();
770            let os_monitors = A::enumerate_display_monitors();
771            for monitor in os_monitors {
772                let ig_mon = ImGuiPlatformMonitor {
773                    MainPos: ImVec2 {
774                        x: monitor.rect.x as f32,
775                        y: monitor.rect.y as f32,
776                    },
777                    MainSize: ImVec2 {
778                        x: monitor.rect.width as f32,
779                        y: monitor.rect.height as f32,
780                    },
781                    WorkPos: ImVec2 {
782                        x: monitor.client_rect.x as f32,
783                        y: monitor.client_rect.y as f32,
784                    },
785                    WorkSize: ImVec2 {
786                        x: monitor.client_rect.width as f32,
787                        y: monitor.client_rect.height as f32,
788                    },
789                    DpiScale: monitor.dpi_scale,
790                };
791                if monitor.primary {
792                    monitors.push(ig_mon)
793                } else {
794                    monitors.insert(0, ig_mon)
795                }
796            }
797
798            platform_io.Monitors.Size = monitors.len() as i32;
799            platform_io.Monitors.Capacity = monitors.len() as i32;
800            platform_io.Monitors.Data = new_monitors(&monitors);
801
802            let vps = &mut *igGetMainViewport();
803
804            // alloc main viewport ViewportData (we obtain from UserData in callbacks)
805            let vp = new_viewport_data::<D, A>();
806            (*vp).main_viewport = true;
807            vps.PlatformUserData = vp as _;
808            vps.PlatformHandle = new_native_handle::<A>(info.main_window.get_native_handle()) as _;
809
810            //
811            let imgui = ImGui {
812                _native_handle: info.main_window.get_native_handle(),
813                _font_texture: font_tex,
814                pipeline,
815                buffers,
816                last_cursor: os::Cursor::None
817            };
818
819            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable as i32) != 0 {
820                imgui.setup_platform_interface();
821            }
822
823            Ok(imgui)
824        }
825    }
826
827    fn setup_platform_interface(&self) {
828        unsafe {
829            let platform_io = &mut *igGetPlatformIO();
830            // platform hooks
831            platform_io.Platform_CreateWindow = Some(platform_create_window::<D, A>);
832            platform_io.Platform_DestroyWindow = Some(platform_destroy_window::<D, A>);
833            platform_io.Platform_ShowWindow = Some(platform_show_window::<D, A>);
834            platform_io.Platform_SetWindowPos = Some(platform_set_window_pos::<D, A>);
835            platform_io.Platform_SetWindowSize = Some(platform_set_window_size::<D, A>);
836            platform_io.Platform_SetWindowFocus = Some(platform_set_window_focus::<D, A>);
837            platform_io.Platform_GetWindowFocus = Some(platform_get_window_focus::<D, A>);
838            platform_io.Platform_GetWindowMinimized = Some(platform_get_window_minimised::<D, A>);
839            platform_io.Platform_SetWindowTitle = Some(platform_set_window_title::<D, A>);
840            platform_io.Platform_GetWindowDpiScale = Some(platform_get_window_dpi_scale::<D, A>);
841            platform_io.Platform_UpdateWindow = Some(platform_update_window::<D, A>);
842
843            // render hooks
844            platform_io.Renderer_RenderWindow = Some(renderer_render_window::<D, A>);
845            platform_io.Renderer_SwapBuffers = Some(renderer_swap_buffers::<D, A>);
846
847            // need to hook these c-compatible getter funtions due to complex return types
848            ImGuiPlatformIO_Set_Platform_GetWindowPos(platform_io, platform_get_window_pos::<D, A>);
849            ImGuiPlatformIO_Set_Platform_GetWindowSize(platform_io, platform_get_window_size::<D, A>)
850        }
851    }
852
853    /// Call this each frame before you want to start making `imgui.` UI calls
854    pub fn new_frame(
855        &mut self,
856        app: &mut A,
857        main_window: &mut A::Window,
858        device: &mut D,
859    ) {
860        let size = main_window.get_size();
861        unsafe {
862            let io = &mut *igGetIO();
863
864            // gotta pack the refs into a pointer and into UserData for callbacks
865            let mut ud = UserData {
866                device,
867                app,
868                main_window,
869                pipeline: &self.pipeline,
870                image_heaps: &Vec::new()
871            };
872            io.UserData = (&mut ud as *mut UserData<D, A>) as _;
873
874            // update display
875            io.DisplaySize = ImVec2 {
876                x: size.x as f32,
877                y: size.y as f32,
878            };
879
880            // update mouse
881            if io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable as i32 == 0 {
882                // non viewports mouse coords are in client space
883                let client_mouse = main_window.get_mouse_client_pos(app.get_mouse_pos());
884                io.MousePos = ImVec2::from(client_mouse);
885            } else {
886                // viewports mouse coords are in screen space
887                io.MousePos = ImVec2::from(app.get_mouse_pos());
888            }
889
890            // set ImGui mouse hovered viewport
891            let platform_io = &mut *igGetPlatformIO();
892            let num_vp = platform_io.Viewports.Size;
893
894            assert_ne!(platform_io.Viewports.Data, std::ptr::null_mut());
895
896            let viewports = std::slice::from_raw_parts(platform_io.Viewports.Data, num_vp as usize);
897
898            // find / reset hovered
899            io.MouseHoveredViewport = 0;
900            for vp in viewports {
901                let p_vp = *vp;
902                let vp_ref = &*p_vp;
903                if !vp_ref.PlatformUserData.is_null() {
904                    let win = get_viewport_window::<D, A>(p_vp);
905                    if win.is_mouse_hovered() && (vp_ref.Flags & ImGuiViewportFlags_NoInputs as i32) == 0 {
906                        io.MouseHoveredViewport = vp_ref.ID;
907                    }
908                }
909            }
910
911            // update mouse
912            io.MouseWheel = app.get_mouse_wheel();
913            io.MouseWheelH = app.get_mouse_wheel();
914            io.MouseDown = app.get_mouse_buttons();
915
916            // update char inputs
917            let utf16 = app.get_utf16_input();
918            for u in utf16 {
919                ImGuiIO_AddInputCharacterUTF16(io, u);
920            }
921
922            // update keyboard
923            let keys_down = app.get_keys_down();
924            std::ptr::copy_nonoverlapping(
925                &keys_down as *const bool,
926                &mut io.KeysDown as *mut bool,
927                256,
928            );
929            io.KeyCtrl = app.is_sys_key_down(os::SysKey::Ctrl);
930            io.KeyShift = app.is_sys_key_down(os::SysKey::Shift);
931            io.KeyAlt = app.is_sys_key_down(os::SysKey::Alt);
932
933            // update mouse cursor
934
935            // Update OS mouse cursor with the cursor requested by imgui
936            let cursor = if io.MouseDrawCursor {
937                to_os_cursor(igGetMouseCursor())
938            } else {
939                os::Cursor::None
940            };
941
942            if self.last_cursor != cursor {
943                self.last_cursor = cursor;
944                app.set_cursor(&self.last_cursor);
945            }
946
947            igNewFrame();
948
949            io.UserData = std::ptr::null_mut();
950        }
951    }
952
953    /// Adds a dockspace to the main window starting at 0,0 running to width, height-status_bar_height
954    /// if status bar height is 0.0 then the whole window will be filled
955    pub fn add_main_dock(&mut self, status_bar_height: f32) {
956        unsafe {
957            // set main window as dockspace
958            let main_viewport = &*igGetMainViewport();
959
960            let size = ImVec2 {
961                x: main_viewport.Size.x,
962                y: main_viewport.Size.y - status_bar_height
963            };
964
965            igSetNextWindowPos(main_viewport.Pos, 0, IMVEC2_ZERO);
966            igSetNextWindowSize(size, 0);
967            igSetNextWindowViewport(main_viewport.ID);
968
969            igPushStyleVarFloat(ImGuiStyleVar_WindowRounding as i32, 0.0);
970            igPushStyleVarFloat(ImGuiStyleVar_WindowBorderSize as i32, 0.0);
971            igPushStyleVarVec2(ImGuiStyleVar_WindowPadding as i32, IMVEC2_ZERO);
972
973            let dockspace_name = "main_window_dockspace\0".as_ptr() as *const i8;
974
975            let mut open = true;
976            igBegin(dockspace_name, &mut open, MAIN_DOCKSPACE_FLAGS as i32);
977
978            let id = igGetIDStr(dockspace_name);
979
980            igDockSpace(id, ImVec2{x: 0.0, y: 0.0}, ImGuiDockNodeFlags_PassthruCentralNode as i32, std::ptr::null_mut());
981
982            igPopStyleVar(3);
983            igEnd();
984
985            // create main dock area
986            igPushStyleVarFloat(ImGuiStyleVar_ChildRounding as i32, 0.0);
987            igPushStyleVarFloat(ImGuiStyleVar_ChildBorderSize as i32, 0.0);
988            igPushStyleVarFloat(ImGuiStyleVar_WindowRounding as i32, 0.0);
989            igPushStyleVarFloat(ImGuiStyleVar_WindowBorderSize as i32, 0.0);
990            igPushStyleVarVec2(ImGuiStyleVar_WindowPadding as i32, IMVEC2_ZERO);
991
992            let window_class = ImGuiWindowClass {
993                DockNodeFlagsOverrideSet: ImGuiDockNodeFlags_NoTabBar,
994                ..Default::default()
995            };
996
997            igSetNextWindowClass(&window_class);
998
999            igBegin(MAIN_DOCK_NAME, std::ptr::null_mut(), 0);
1000
1001            let mut avail = IMVEC2_ZERO;
1002            igGetContentRegionAvail(&mut avail);
1003
1004            igEnd();
1005            igPopStyleVar(5);
1006        }
1007    }
1008
1009    /// Add an optional status bar which appears at the bottom of the main window at fixed size and position
1010    /// you push items into the status bar by using `imgui.begin("status_bar")` 
1011    /// when passing `height` the true size may actually differ, the actual size is returned from this function
1012    pub fn add_status_bar(&mut self, height: f32) -> f32 {
1013        unsafe {
1014            let status_bar_flags = ImGuiWindowFlags_NoDocking | 
1015                ImGuiWindowFlags_NoResize | 
1016                ImGuiWindowFlags_NoTitleBar |
1017                ImGuiWindowFlags_NoMove | 
1018                ImGuiWindowFlags_NoScrollbar | 
1019                ImGuiWindowFlags_NoSavedSettings;
1020
1021            let vp = &*igGetMainViewport();
1022            let style = &*igGetStyle();
1023
1024            igSetNextWindowPos(ImVec2 {x: vp.Pos.x, y: vp.Pos.y + vp.Size.y - height}, 0, IMVEC2_ZERO);
1025            igSetNextWindowSize(ImVec2 {x: vp.Size.x, y: height}, 0);
1026            igPushStyleVarFloat(ImGuiStyleVar_WindowRounding as i32, 0.0);
1027            igPushStyleVarFloat(ImGuiStyleVar_WindowBorderSize as i32, 0.0);
1028            igPushStyleColorVec4(ImGuiCol_WindowBg as i32, style.Colors[ImGuiCol_MenuBarBg as usize]);
1029
1030            igBegin(STATUS_BAR_NAME, std::ptr::null_mut(), status_bar_flags as i32);
1031            igPopStyleColor(1);
1032            igPopStyleVar(2);
1033
1034            let mut actual_size = IMVEC2_ZERO;
1035            igGetWindowSize(&mut actual_size);
1036            let actual_height = actual_size.y;
1037
1038            igEnd();
1039
1040            actual_height
1041        }
1042    }
1043
1044    /// Return the size of the main dockspace viewport which is the background of the main window
1045    pub fn get_main_dock_size(&self) -> (f32, f32) {
1046        unsafe {
1047            igBegin(MAIN_DOCK_NAME, std::ptr::null_mut(), 0);
1048            let mut avail = IMVEC2_ZERO;
1049            igGetContentRegionAvail(&mut avail);
1050            igEnd();
1051            (avail.x, avail.y)
1052        }
1053    }
1054
1055    /// Call this each frame to render the `ImGui` data, `image_heaps` can be empty if you are only
1056    /// using the default device heap via `device.create_texture`
1057    pub fn render(
1058        &mut self,
1059        app: &mut A,
1060        main_window: &mut A::Window,
1061        device: &mut D,
1062        cmd: &mut D::CmdBuf,
1063        image_heaps: &Vec<&D::Heap>,
1064    ) {
1065        unsafe {
1066            let io = &mut *igGetIO();
1067            igRender();
1068
1069            render_draw_data::<D>(
1070                &*igGetDrawData(),
1071                device,
1072                cmd,
1073                image_heaps,
1074                &mut self.buffers,
1075                &self.pipeline,
1076            )
1077            .unwrap();
1078
1079            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable as i32) != 0 {
1080                // gotta pack the refs into a pointer and into io.UserData
1081                let mut ud = UserData {
1082                    device,
1083                    app,
1084                    main_window,
1085                    pipeline: &self.pipeline,
1086                    image_heaps
1087                };
1088                io.UserData = (&mut ud as *mut UserData<D, A>) as _;
1089
1090                igUpdatePlatformWindows();
1091                igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
1092
1093                io.UserData = std::ptr::null_mut();
1094            }
1095        }
1096    }
1097
1098    /// This is the native `ImGui` demo window which showcases all features present in `ImGui`
1099    pub fn demo(&self) {
1100        unsafe {
1101            static mut SHOW_DEMO_WINDOW: bool = true;
1102            static mut SHOW_ANOTHER_WINDOW: bool = true;
1103            static mut CLEAR_COLOUR: [f32; 3] = [0.0, 0.0, 0.0];
1104
1105            let io = &mut *igGetIO();
1106
1107            if SHOW_DEMO_WINDOW {
1108                igShowDemoWindow(static_ref_mut!(SHOW_DEMO_WINDOW));
1109            }
1110
1111            // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
1112            {
1113                static mut SLIDER_FLOAT: f32 = 0.0;
1114                static mut COUNTER: i32 = 0;
1115
1116                let mut open = true;
1117
1118                // Create a window called "Hello, world!" and append into it.
1119                igBegin(
1120                    "Hello, world!\0".as_ptr() as *const i8,
1121                    &mut open,
1122                    ImGuiWindowFlags_None as i32,
1123                );
1124
1125                igText(
1126                    "%s\0".as_ptr() as *const i8,
1127                    "This is some useful text.\0".as_ptr() as *const i8,
1128                );
1129
1130                igCheckbox("Demo Window\0".as_ptr() as *const i8, static_ref_mut!(SHOW_DEMO_WINDOW));
1131                igCheckbox("Another Window\0".as_ptr() as *const i8, static_ref_mut!(SHOW_ANOTHER_WINDOW));
1132
1133                igText(
1134                    "%f, %f : %f %f\0".as_ptr() as *const i8,
1135                    io.MousePos.x as f64,
1136                    io.MousePos.y as f64,
1137                    io.DisplaySize.x as f64,
1138                    io.DisplaySize.y as f64,
1139                );
1140
1141                igSliderFloat(
1142                    "float\0".as_ptr() as _,
1143                    static_ref_mut!(SLIDER_FLOAT),
1144                    0.0,
1145                    1.0,
1146                    "%.3f\0".as_ptr() as _,
1147                    0,
1148                );
1149
1150                if igButton("Button\0".as_ptr() as _, ImVec2 { x: 0.0, y: 0.0 }) {
1151                    COUNTER += 1;
1152                }
1153
1154                igSameLine(0.0, -1.0);
1155
1156                igText(
1157                    "counter = %i\0".as_ptr() as *const i8,
1158                    "This is some useful text.\0".as_ptr() as *const i8,
1159                );
1160
1161                igColorEdit3("clear color\0".as_ptr() as _, CLEAR_COLOUR.as_mut_ptr(), 0); // Edit 3 floats representing a color
1162
1163                igText(
1164                    "Application average %.3f ms/frame (%.1f FPS)\0".as_ptr() as _,
1165                    1000.0 / io.Framerate as f64,
1166                    io.Framerate as f64,
1167                );
1168
1169                igEnd();
1170            }
1171
1172            // 3. Show another simple window.
1173            if SHOW_ANOTHER_WINDOW {
1174                igBegin(
1175                    "Another Window\0".as_ptr() as *const i8,
1176                    static_ref_mut!(SHOW_ANOTHER_WINDOW),
1177                    ImGuiWindowFlags_None as i32,
1178                );
1179
1180                igText(
1181                    "%s\0".as_ptr() as *const i8,
1182                    "Hello from another window!\0".as_ptr() as *const i8,
1183                );
1184
1185                if igButton("Close Me\0".as_ptr() as _, ImVec2 { x: 0.0, y: 0.0 }) {
1186                    SHOW_ANOTHER_WINDOW = false;
1187                }
1188
1189                igEnd();
1190            }
1191        }
1192    }
1193
1194    /// Get the current imgui context so it can be passed to other plugins and libs, to be used woth `set_current_context`
1195    pub fn get_current_context(&self) ->*mut core::ffi::c_void {
1196        unsafe {
1197            igGetCurrentContext() as *mut core::ffi::c_void
1198        }
1199    }
1200
1201    /// This function will make the `ImGuiContext` current, required when calling imgui from inside in a plugin dll or lib
1202    pub fn set_current_context(&mut self, context: *mut core::ffi::c_void) {
1203        unsafe {
1204            igSetCurrentContext(context as *mut ImGuiContext);
1205        }
1206    }
1207
1208    /// Begin a new imgui window
1209    pub fn begin(&mut self, title: &str, open: &mut bool, flags: WindowFlags) -> bool {
1210        unsafe {
1211            let null_title = CString::new(title).unwrap();
1212            igBegin(
1213                null_title.as_ptr() as *const i8,
1214                open as *mut bool,
1215                i32::from(flags),
1216            )
1217        }
1218    }
1219
1220    /// Begin a window with no settings, you can use this to re-use existing and setup windows
1221    pub fn begin_window(&mut self, name: &str) -> bool {
1222        unsafe {
1223            let null_name = CString::new(name).unwrap();
1224            igBegin(
1225                null_name.as_ptr() as *const i8,
1226                std::ptr::null_mut(),
1227                0
1228            )
1229        }
1230    }
1231
1232    /// End imgui window must be called after a call to `begin` regardless of if `begin` returns true or false
1233    pub fn end(&mut self) {
1234        unsafe { 
1235            igEnd();
1236        };
1237    }
1238
1239    /// Add imgui text widget, you can format text to pass in: `imgui.text(&format!("{}", values));`
1240    pub fn text(&mut self, text: &str) {
1241        let null_term_text = CString::new(text).unwrap();
1242        unsafe {
1243            igText(null_term_text.as_ptr() as *const i8);
1244        }
1245    }
1246
1247    /// Add coloured text specified with rgba 0-1 range float.
1248    pub fn colour_text(&mut self, text: &str, col: Vec4f) {
1249        unsafe {
1250            igPushStyleColorVec4(ImGuiCol_Text as i32, to_im_vec4(col));
1251            self.text(text);
1252            igPopStyleColor(1);
1253        }
1254    }
1255
1256    /// Push a style colour using ImGuiCol_ flags
1257    pub fn push_style_colour(&mut self, flags: ImGuiStyleVar, col: Vec4f) {
1258        unsafe {
1259            igPushStyleColorVec4(flags, to_im_vec4(col))
1260        }
1261    }
1262
1263    /// Pop a single style colour ImGuiStyleVar from the stack
1264    pub fn pop_style_colour(&mut self) {
1265        unsafe {
1266            igPopStyleColor(1);
1267        }
1268    }
1269    
1270    /// Pop a style colour using ImGuiCol_ flags
1271    pub fn pop_style_colour_count(&mut self, count: i32) {
1272        unsafe {
1273            igPopStyleColor(count);
1274        }
1275    }
1276
1277    /// Begin imgui main menu bar which appears at the top of the main window
1278    pub fn begin_main_menu_bar(&mut self) -> bool {
1279        unsafe {
1280            igBeginMainMenuBar()
1281        }
1282    }
1283
1284    /// Ends main menu bar calls, this must be called after a call to `begin_main_menu_bar` returns true
1285    pub fn end_main_menu_bar(&mut self) {
1286        unsafe {
1287            igEndMainMenuBar()
1288        }
1289    }
1290
1291    /// Begin new menu, populate it with `menu_item`
1292    pub fn begin_menu(&mut self, label: &str) -> bool {
1293        let null_term_label = CString::new(label).unwrap();
1294        unsafe {
1295            igBeginMenu(null_term_label.as_ptr() as *const i8, true)
1296        }
1297    }
1298
1299    /// End menu on the top of the stack, pairs with `begin_menu`
1300    pub fn end_menu(&mut self) {
1301        unsafe {
1302            igEndMenu()
1303        }
1304    }
1305
1306    /// Begin a combo box, populate it with `selectable`'s
1307    pub fn begin_combo(&mut self, label: &str, preview_item: &str, flags: ImGuiComboFlags) -> bool {
1308        unsafe {
1309            let null_term_label = CString::new(label).unwrap();
1310            let null_term_preview_item = CString::new(preview_item).unwrap();
1311            igBeginCombo(
1312                null_term_label.as_ptr() as *const i8, 
1313                null_term_preview_item.as_ptr() as *const i8,
1314                flags
1315            )
1316        }
1317    }
1318
1319    /// End combo box on the top of the stack, pairs with `begin_combo`
1320    pub fn end_combo(&mut self) {
1321        unsafe {
1322            igEndCombo()
1323        }
1324    }
1325
1326    /// A selectable item, can be used in menus, combo boxes, list etc. it highlights on hover
1327    pub fn selectable(&mut self, label: &str, selected: bool, flags: ImGuiSelectableFlags) -> bool {
1328        unsafe {
1329            let null_term_label = CString::new(label).unwrap();
1330            igSelectableBool(null_term_label.as_ptr() as *const i8, selected, flags, ImVec2 { x: 0.0, y: 0.0 })
1331        }
1332    }
1333
1334    /// Simple combo box generated from a `Vec<String>`
1335    pub fn combo_list(&mut self, label: &str, items: &Vec<String>, selected: &str) -> (bool, String) {
1336        let mut result = selected.to_string();
1337        if self.begin_combo(label, selected, ImGuiComboFlags_None as i32) {
1338            for item in items {
1339                if self.selectable(item, item == selected, ImGuiSelectableFlags_None as i32) {
1340                    result = item.to_string(); 
1341                }
1342            }
1343            self.end_combo();
1344            (true, result)
1345        }
1346        else {
1347            (false, result)
1348        }
1349    }
1350
1351    /// Menu items to populate menus between `begin_menu` and `end_menu`
1352    pub fn menu_item(&mut self, label: &str) -> bool {
1353        let null_term_label = CString::new(label).unwrap();
1354        unsafe {
1355            igMenuItemBool(
1356                null_term_label.as_ptr() as *const i8, 
1357                std::ptr::null(), 
1358                false, 
1359                true)
1360        }
1361    }
1362
1363    /// Adds a horizontal line separating items
1364    pub fn separator(&mut self) {
1365        unsafe {
1366            igSeparator()
1367        }
1368    }
1369
1370    /// Adds horizontal spacing
1371    pub fn spacing(&mut self) {
1372        unsafe {
1373            igSpacing()
1374        }
1375    }
1376
1377    /// The next width will appear on the same line (horizontally) as the previous
1378    pub fn same_line(&mut self) {
1379        unsafe { 
1380            igSameLine(0.0, -1.0);
1381        };
1382    }
1383
1384    /// Button with label, auto size
1385    pub fn button(&mut self, label: &str) -> bool {
1386        unsafe {
1387            let null_label = CString::new(label).unwrap();
1388            igButton(null_label.as_ptr() as *const i8, ImVec2{x: 0.0, y: 0.0})
1389        }
1390    }
1391
1392    // Button with lable at specified size, supply 0.0 to `w` or `h` to auto size the dimension
1393    pub fn button_size(&mut self, label: &str, w: f32, h: f32) -> bool {
1394        unsafe {
1395            let null_label = CString::new(label).unwrap();
1396            igButton(null_label.as_ptr() as *const i8, ImVec2{x: w, y: h})
1397        }
1398    }
1399
1400    /// Checkbox can be used on bools yes/no will display a tick or check when v is true and appear
1401    /// empty otherwise
1402    pub fn checkbox(&mut self, label: &str, v: &mut bool) -> bool {
1403        unsafe {    
1404            let null_label = CString::new(label).unwrap();
1405            igCheckbox(null_label.as_ptr() as *const i8, v)
1406        }
1407    }
1408
1409    /// Modify float via keyboard input
1410    pub fn input_float(&mut self, label: &str, v: &mut f32) -> bool {
1411        unsafe {
1412            let null_label = CString::new(label).unwrap();
1413            let fmt = CString::new("%.3f").unwrap();
1414            igInputFloat(
1415                null_label.as_ptr() as *const i8, v, 0.0, 0.0, fmt.as_ptr() as *const i8, 0)
1416        }
1417    }
1418
1419    /// Modify int via keyboard input
1420    pub fn input_int(&mut self, label: &str, v: &mut i32) -> bool {
1421        unsafe {
1422            let null_label = CString::new(label).unwrap();
1423            igInputInt(
1424                null_label.as_ptr() as *const i8, v, 0, 0, 0)
1425        }
1426    }
1427
1428    /// Modify float via slider input
1429    pub fn slider_float(&mut self, label: &str, v: &mut f32, min: f32, max: f32) -> bool {
1430        unsafe {
1431            let null_label = CString::new(label).unwrap();
1432            let fmt = CString::new("%.3f").unwrap();
1433            igSliderFloat(
1434                null_label.as_ptr() as *const i8, v, min, max, fmt.as_ptr() as *const i8, 0)
1435        }
1436    }
1437
1438    /// Adds a dummy widget for custom padding
1439    pub fn dummy(&mut self, w: f32, h: f32) {
1440        unsafe {
1441            igDummy(ImVec2 {x: w, y: h})
1442        }
1443    }
1444
1445    /// Adds a dummy widget for custom padding horizontally
1446    pub fn hdummy(&mut self, size: f32) {
1447        unsafe {
1448            igDummy(ImVec2 {x: size, y: 0.0})
1449        }
1450    }
1451
1452    /// Adds a dummy widget for custom padding vertically
1453    pub fn vdummy(&mut self, size: f32) {
1454        unsafe {
1455            igDummy(ImVec2 {x: 0.0, y: size})
1456        }
1457    }
1458
1459    /// Dsiaply an image at the supplied width `w` and height `h` in pixels
1460    pub fn image(&mut self, tex: &D::Texture, w: f32, h: f32) {
1461        unsafe {
1462            let id = to_imgui_texture_id::<D>(tex);
1463            igImage(
1464                id, 
1465                ImVec2 {x: w, y: h},
1466                ImVec2 {x: 0.0, y: 0.0},
1467                ImVec2 {x: 1.0, y: 1.0},
1468                ImVec4 {x: 1.0, y: 1.0, z: 1.0, w: 1.0},
1469                ImVec4 {x: 0.0, y: 0.0, z: 0.0, w: 0.0},
1470            );
1471        }
1472    }
1473
1474    /// Calculates the `x`, `y` size of the given text string, it can be used to aid layouts.
1475    pub fn calc_text_size(&self, text: &str) -> (f32, f32) {
1476        let null_text = CString::new(text).unwrap();
1477        let mut size = IMVEC2_ZERO;
1478        unsafe {
1479            igCalcTextSize(&mut size, null_text.as_ptr() as *const i8, std::ptr::null(), false, -1.0);
1480        }
1481        (size.x, size.y)
1482    }
1483
1484    /// Align the next item to right of the context region subtarcted `offset` toward the left
1485    pub fn right_align(&mut self, offset: f32) {
1486        unsafe {
1487            let mut size = IMVEC2_ZERO;
1488            igGetWindowSize(&mut size);
1489            igSetCursorPosX(size.x - offset);
1490        }
1491    }
1492
1493    /// Fills an entire content region identified by `label` with an image. Displ;aying no boarders or padding
1494    pub fn image_window(&mut self, label: &str, tex: &D::Texture) {
1495        unsafe {
1496            let null_label = CString::new(label).unwrap();
1497
1498            let (w, h) = self.get_main_dock_size();
1499
1500            let id = to_imgui_texture_id::<D>(tex);
1501
1502            igBegin(null_label.as_ptr() as *const i8, std::ptr::null_mut(), 0);
1503            
1504            igImage(
1505                id, 
1506                ImVec2 {x: w, y: h},
1507                ImVec2 {x: 0.0, y: 0.0},
1508                ImVec2 {x: 1.0, y: 1.0},
1509                ImVec4 {x: 1.0, y: 1.0, z: 1.0, w: 1.0},
1510                ImVec4 {x: 0.0, y: 0.0, z: 0.0, w: 0.0},
1511            );
1512
1513            igEnd();
1514        }
1515    }
1516
1517    /// Sets the item of the next widget > 0.0 = width on pixels. < 0.0 = align to the right of window
1518    pub fn set_next_item_width(&mut self, item_width: f32) {
1519        unsafe {
1520            igSetNextItemWidth(item_width);
1521        }
1522    }
1523
1524    /// Returns true if the main dock is hovered fals otherwise
1525    pub fn main_dock_hovered(&self) -> bool {
1526        unsafe {
1527            igBegin(MAIN_DOCK_NAME, std::ptr::null_mut(), 0);
1528            let result = igIsWindowHovered(0);
1529            igEnd();
1530            result
1531        }
1532    }
1533
1534    /// Returns true if `ImGui` wants to intercept keyboard events, so they can be ignored elsewhere
1535    pub fn want_capture_keyboard(&self) -> bool {
1536        unsafe {
1537            let io = &mut *igGetIO();
1538            io.WantCaptureKeyboard
1539        }
1540    }
1541
1542    /// Returns true if `ImGui` wants to intercept mouse events, so they can be ignored elsewhere
1543    pub fn want_capture_mouse(&self) -> bool {
1544        unsafe {
1545            let io = &mut *igGetIO();
1546            io.WantCaptureMouse
1547        }
1548    }
1549
1550    /// Saves `imgui.ini` to the executable directory
1551    pub fn save_ini_settings(&self) {
1552        unsafe {
1553            let io = &mut *igGetIO();
1554            igSaveIniSettingsToDisk(io.IniFilename);
1555        }
1556    }
1557
1558    /// Saves `imgui.ini` to the specified directory `path`
1559    pub fn save_ini_settings_to_location(&self, path: &str) {
1560        unsafe {
1561            let null_term_filename = CString::new(format!("{}/imgui.ini", path)).unwrap();
1562            igSaveIniSettingsToDisk(null_term_filename.as_ptr() as *const i8);
1563        }
1564    }
1565}
1566
1567impl<D, A> Drop for ImGui<D, A> where D: Device, A: App {
1568    fn drop(&mut self) {
1569        unsafe {
1570            igDestroyPlatformWindows();
1571            let platform_io = &mut *igGetPlatformIO();
1572            std::ptr::drop_in_place(platform_io.Monitors.Data as *mut ImGuiPlatformMonitor);
1573            platform_io.Monitors.Data = std::ptr::null_mut();
1574        }
1575    }
1576}
1577
1578impl From<os::Point<i32>> for ImVec2 {
1579    fn from(point: os::Point<i32>) -> ImVec2 {
1580        ImVec2 {
1581            x: point.x as f32,
1582            y: point.y as f32,
1583        }
1584    }
1585}
1586
1587impl From<ImVec2> for os::Point<i32> {
1588    fn from(vec2: ImVec2) -> os::Point<i32> {
1589        os::Point {
1590            x: vec2.x as i32,
1591            y: vec2.y as i32,
1592        }
1593    }
1594}
1595
1596/// handles the case where we can return an imgui created window from ViewportData, or borrow the main window
1597/// from UserData
1598fn get_viewport_window<'a, D: Device, A: App>(vp: *mut ImGuiViewport) -> &'a mut A::Window {
1599    unsafe {
1600        let vp_ref = &mut *vp;
1601        let vd = &mut *(vp_ref.PlatformUserData as *mut ViewportData<D, A>);
1602        if vd.main_viewport {
1603            let io = &mut *igGetIO();
1604            let ud = &mut *(io.UserData as *mut UserData<D, A>);
1605            return ud.main_window;
1606        }
1607        &mut vd.window[0]
1608    }
1609}
1610
1611/// get a hotline_rs::imgui::ViewportData mutable reference from an ImGuiViewport
1612fn get_viewport_data<'a, D: Device, A: App>(vp: *mut ImGuiViewport) -> &'a mut ViewportData<D, A> {
1613    unsafe {
1614        let vp_ref = &mut *vp;
1615        &mut *(vp_ref.PlatformUserData as *mut ViewportData<D, A>)
1616    }
1617}
1618
1619/// Get the UserData packed inside ImGui.io to access Device, App and main Window
1620fn get_user_data<'a, D: Device, A: App>() -> &'a mut UserData<'a, D, A> {
1621    unsafe {
1622        let io = &mut *igGetIO();
1623        &mut *(io.UserData as *mut UserData<D, A>)
1624    }
1625}
1626
1627unsafe extern "C" fn platform_create_window<D: Device, A: App>(vp: *mut ImGuiViewport) {
1628    let io = &mut *igGetIO();
1629    let ud = &mut *(io.UserData as *mut UserData<D, A>);
1630    let device = &mut ud.device;
1631    let vp_ref = &mut *vp;
1632
1633    // alloc viewport data
1634    let p_vd = new_viewport_data::<D, A>();
1635    let vd = &mut *p_vd;
1636
1637    // find parent
1638    let mut parent_handle = None;
1639    if vp_ref.ParentViewportId != 0 {
1640        let parent = &*igFindViewportByID(vp_ref.ParentViewportId);
1641        let nh = &*(parent.PlatformHandle as *mut A::NativeHandle);
1642        parent_handle = Some(nh.copy());
1643    }
1644
1645    // create a window
1646    vd.window = vec![ud.app.create_window(os::WindowInfo {
1647        title: String::from("Utitled"),
1648        rect: os::Rect {
1649            x: vp_ref.Pos.x as i32,
1650            y: vp_ref.Pos.y as i32,
1651            width: vp_ref.Size.x as i32,
1652            height: vp_ref.Size.y as i32,
1653        },
1654        style: os::WindowStyleFlags::from(vp_ref.Flags),
1655        parent_handle,
1656    })];
1657
1658    // create cmd buffer
1659    vd.cmd = vec![device.create_cmd_buf(2)];
1660
1661    // create swap chain and bind to window
1662    let swap_chain_info = gfx::SwapChainInfo {
1663        num_buffers: 2,
1664        format: gfx::Format::RGBA8n,
1665        clear_colour: Some(gfx::ClearColour {
1666            r: 0.45,
1667            g: 0.55,
1668            b: 0.60,
1669            a: 1.00,
1670        }),
1671    };
1672    vd.swap_chain = vec![device.create_swap_chain::<A>(&swap_chain_info, &vd.window[0]).unwrap()];
1673
1674    // create render buffers
1675    let mut buffers: Vec<RenderBuffers<D>> = Vec::new();
1676    let num_buffers = vd.swap_chain[0].get_num_buffers();
1677    for _i in 0..num_buffers {
1678        buffers.push(RenderBuffers {
1679            vb: create_vertex_buffer::<D>(device, DEFAULT_VB_SIZE).unwrap(),
1680            vb_size: DEFAULT_VB_SIZE,
1681            ib: create_index_buffer::<D>(device, DEFAULT_IB_SIZE).unwrap(),
1682            ib_size: DEFAULT_IB_SIZE,
1683        })
1684    }
1685    vd.buffers = buffers;
1686
1687    // track the viewport user data pointer
1688    vp_ref.PlatformUserData = p_vd as *mut _;
1689    vp_ref.PlatformRequestResize = false;
1690    vp_ref.PlatformHandle = new_native_handle::<A>(vd.window[0].get_native_handle()) as _;
1691}
1692
1693unsafe extern "C" fn platform_destroy_window<D: Device, A: App>(vp: *mut ImGuiViewport) {
1694    let vd = get_viewport_data::<D, A>(vp);
1695    let vp_ref = &mut *vp;
1696
1697    if !vd.swap_chain.is_empty() {
1698        vd.swap_chain[0].wait_for_last_frame();
1699        vd.cmd[0].reset(&vd.swap_chain[0]);
1700    }
1701
1702    // unregister window tracking... if UserData is null we are shutting down
1703    let io = &mut *igGetIO();
1704    if !io.UserData.is_null() {
1705        get_user_data::<D, A>().app.destroy_window(&vd.window[0]);
1706    }
1707    
1708    if !vd.swap_chain.is_empty() {
1709        vd.swap_chain.clear();
1710    }
1711
1712    if !vd.cmd.is_empty() {
1713        vd.cmd.clear();
1714    }
1715
1716    if !vd.buffers.is_empty() {
1717        vd.buffers.clear();
1718    }
1719    
1720    if !vd.window.is_empty() {
1721        vd.window.clear();
1722    }
1723
1724    // drop and null allocated data
1725    std::ptr::drop_in_place(vp_ref.PlatformUserData as *mut ViewportData<D, A>);
1726    vp_ref.PlatformUserData = std::ptr::null_mut();
1727    std::ptr::drop_in_place(vp_ref.PlatformHandle as *mut A::NativeHandle);
1728    vp_ref.PlatformHandle = std::ptr::null_mut();
1729}
1730
1731unsafe extern "C" fn platform_update_window<D: Device, A: App>(vp: *mut ImGuiViewport) {
1732    let window = get_viewport_window::<D, A>(vp);
1733    let vp_ref = &mut *vp;
1734    window.update(get_user_data::<D, A>().app);
1735    window.update_style(
1736        os::WindowStyleFlags::from(vp_ref.Flags),
1737        os::Rect {
1738            x: vp_ref.Pos.x as i32,
1739            y: vp_ref.Pos.y as i32,
1740            width: vp_ref.Size.x as i32,
1741            height: vp_ref.Size.y as i32,
1742        },
1743    );
1744    let events = window.get_events();
1745    if events.contains(os::WindowEventFlags::CLOSE) {
1746        vp_ref.PlatformRequestClose = true;
1747    }
1748    if events.contains(os::WindowEventFlags::MOVE) {
1749        vp_ref.PlatformRequestMove = true;
1750    }
1751    if events.contains(os::WindowEventFlags::SIZE) {
1752        vp_ref.PlatformRequestResize = true;
1753    }
1754    window.clear_events();
1755}
1756
1757unsafe extern "C" fn platform_get_window_pos<D: Device, A: App>(vp: *mut ImGuiViewport, out_pos: *mut ImVec2) {
1758    let window = get_viewport_window::<D, A>(vp);
1759    let pos = window.get_pos();
1760    (*out_pos).x = pos.x as f32;
1761    (*out_pos).y = pos.y as f32;
1762}
1763
1764unsafe extern "C" fn platform_get_window_size<D: Device, A: App>(vp: *mut ImGuiViewport, out_size: *mut ImVec2) {
1765    let window = get_viewport_window::<D, A>(vp);
1766    let size = window.get_size();
1767    (*out_size).x = size.x as f32;
1768    (*out_size).y = size.y as f32;
1769}
1770
1771unsafe extern "C" fn platform_show_window<D: Device, A: App>(vp: *mut ImGuiViewport) {
1772    let window = get_viewport_window::<D, A>(vp);
1773    let activate = (*vp).Flags & ImGuiViewportFlags_NoFocusOnAppearing as i32 == 0;
1774    window.show(true, activate);
1775}
1776
1777unsafe extern "C" fn platform_set_window_title<D: Device, A: App>(vp: *mut ImGuiViewport, str_: *const cty::c_char) {
1778    let win = get_viewport_window::<D, A>(vp);
1779    let cstr = CStr::from_ptr(str_);
1780    win.set_title(String::from(cstr.to_str().unwrap()));
1781}
1782
1783unsafe extern "C" fn platform_set_window_focus<D: Device, A: App>(vp: *mut ImGuiViewport) {
1784    let window = get_viewport_window::<D, A>(vp);
1785    window.set_focused();
1786}
1787
1788unsafe extern "C" fn platform_get_window_focus<D: Device, A: App>(vp: *mut ImGuiViewport) -> bool {
1789    let window = get_viewport_window::<D, A>(vp);
1790    window.is_focused()
1791}
1792
1793unsafe extern "C" fn platform_set_window_pos<D: Device, A: App>(vp: *mut ImGuiViewport, pos: ImVec2) {
1794    let window = get_viewport_window::<D, A>(vp);
1795    window.set_pos(os::Point::from(pos));
1796}
1797
1798unsafe extern "C" fn platform_set_window_size<D: Device, A: App>(vp: *mut ImGuiViewport, size: ImVec2) {
1799    let window = get_viewport_window::<D, A>(vp);
1800    window.set_size(os::Size::from(size));
1801}
1802
1803unsafe extern "C" fn platform_get_window_minimised<D: Device, A: App>(vp: *mut ImGuiViewport) -> bool {
1804    let window = get_viewport_window::<D, A>(vp);
1805    window.is_minimised()
1806}
1807
1808unsafe extern "C" fn platform_get_window_dpi_scale<D: Device, A: App>(vp: *mut ImGuiViewport) -> f32 {
1809    let window = get_viewport_window::<D, A>(vp);
1810    window.get_dpi_scale()
1811}
1812
1813unsafe extern "C" fn renderer_render_window<D: Device, A: App>(vp: *mut ImGuiViewport, _render_arg: *mut cty::c_void) where D::RenderPipeline: gfx::Pipeline {
1814    let ud = get_user_data::<D, A>();
1815    let vd = get_viewport_data::<D, A>(vp);
1816    let vp_ref = &*vp;
1817
1818    // must be an imgui created window
1819    assert_ne!(vd.window.len(), 0);
1820    assert_ne!(vd.cmd.len(), 0);
1821    assert_ne!(vd.swap_chain.len(), 0);
1822
1823    // unpack from vec
1824    let window = &mut vd.window[0];
1825    let cmd = &mut vd.cmd[0];
1826    let swap = &mut vd.swap_chain[0];
1827    let vp_rect = window.get_viewport_rect();
1828
1829    // update
1830    window.update(ud.app);
1831    swap.update::<A>(ud.device, window, cmd);
1832    cmd.reset(swap);
1833
1834    // render
1835    let viewport = gfx::Viewport::from(vp_rect);
1836    let scissor = gfx::ScissorRect::from(vp_rect);
1837
1838    // TODO:
1839    cmd.transition_barrier(&gfx::TransitionBarrier {
1840        texture: Some(swap.get_backbuffer_texture()),
1841        buffer: None,
1842        state_before: gfx::ResourceState::Present,
1843        state_after: gfx::ResourceState::RenderTarget,
1844    });
1845
1846    let pass = swap.get_backbuffer_pass_mut();
1847    cmd.begin_render_pass(pass);
1848
1849    cmd.set_viewport(&viewport);
1850    cmd.set_scissor_rect(&scissor);
1851
1852    render_draw_data::<D>(
1853        &*vp_ref.DrawData,
1854        ud.device,
1855        cmd,
1856        ud.image_heaps,
1857        &mut vd.buffers,
1858        ud.pipeline,
1859    )
1860    .unwrap();
1861
1862    cmd.end_render_pass();
1863
1864    // TODO:
1865    cmd.transition_barrier(&gfx::TransitionBarrier {
1866        texture: Some(swap.get_backbuffer_texture()),
1867        buffer: None,
1868        state_before: gfx::ResourceState::RenderTarget,
1869        state_after: gfx::ResourceState::Present,
1870    });
1871
1872    cmd.close().unwrap();
1873
1874    ud.device.execute(cmd);
1875}
1876
1877unsafe extern "C" fn renderer_swap_buffers<D: Device, A: App>(vp: *mut ImGuiViewport, _render_arg: *mut cty::c_void) {
1878    let ud = get_user_data::<D, A>();
1879    let vd = get_viewport_data::<D, A>(vp);
1880    assert_ne!(vd.swap_chain.len(), 0);
1881    vd.swap_chain[0].swap(ud.device);
1882}
1883
1884pub type WindowSizeCallback = unsafe extern "C" fn(vp: *mut ImGuiViewport, out_pos: *mut ImVec2);
1885
1886extern "C" {
1887    pub fn ImGuiPlatformIO_Set_Platform_GetWindowPos(
1888        platform_io: *mut ImGuiPlatformIO,
1889        function: WindowSizeCallback,
1890    );
1891
1892    pub fn ImGuiPlatformIO_Set_Platform_GetWindowSize(
1893        platform_io: *mut ImGuiPlatformIO,
1894        function: WindowSizeCallback,
1895    );
1896}
1897
1898impl From<ImGuiViewportFlags> for os::WindowStyleFlags {
1899    fn from(flags: ImGuiViewportFlags) -> os::WindowStyleFlags {
1900        let mut style = os::WindowStyleFlags::IMGUI;
1901        if (flags & ImGuiViewportFlags_NoDecoration as i32) != 0 {
1902            style |= os::WindowStyleFlags::POPUP;
1903        } else {
1904            style |= os::WindowStyleFlags::OVERLAPPED_WINDOW;
1905        }
1906        if (flags & ImGuiViewportFlags_NoTaskBarIcon as i32) != 0 {
1907            style |= os::WindowStyleFlags::TOOL_WINDOW;
1908        } else {
1909            style |= os::WindowStyleFlags::APP_WINDOW;
1910        }
1911        if (flags & ImGuiViewportFlags_TopMost as i32) != 0 {
1912            style |= os::WindowStyleFlags::TOPMOST;
1913        }
1914        style
1915    }
1916}
1917
1918#[allow(non_upper_case_globals)]
1919const fn to_os_cursor(cursor: ImGuiMouseCursor) -> os::Cursor {
1920    match cursor {
1921        ImGuiMouseCursor_Arrow => os::Cursor::Arrow,
1922        ImGuiMouseCursor_TextInput => os::Cursor::TextInput,
1923        ImGuiMouseCursor_ResizeAll => os::Cursor::ResizeAll,
1924        ImGuiMouseCursor_ResizeEW => os::Cursor::ResizeEW,
1925        ImGuiMouseCursor_ResizeNS => os::Cursor::ResizeNS,
1926        ImGuiMouseCursor_ResizeNESW => os::Cursor::ResizeNESW,
1927        ImGuiMouseCursor_ResizeNWSE => os::Cursor::ResizeNWSE,
1928        ImGuiMouseCursor_Hand => os::Cursor::Hand,
1929        ImGuiMouseCursor_NotAllowed => os::Cursor::NotAllowed,
1930        _ => os::Cursor::None,
1931    }
1932}