summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/fragment.frag7
-rw-r--r--src/bin/phosphor_test.rs89
-rw-r--r--src/bin/vertex.vert9
-rw-r--r--src/keyboard_input.rs19
-rw-r--r--src/lib.rs29
-rw-r--r--src/press_state.rs26
-rw-r--r--src/program_controller.rs13
-rw-r--r--src/render_hint.rs33
-rw-r--r--src/render_request.rs40
-rw-r--r--src/window.rs136
-rw-r--r--src/window/x11.rs66
-rw-r--r--src/window_controller.rs42
-rw-r--r--src/window_manager.rs156
13 files changed, 665 insertions, 0 deletions
diff --git a/src/bin/fragment.frag b/src/bin/fragment.frag
new file mode 100644
index 0000000..99826f6
--- /dev/null
+++ b/src/bin/fragment.frag
@@ -0,0 +1,7 @@
+#version 330
+out vec4 FragColor;
+in vec3 vertexColor;
+
+void main() {
+ FragColor = vec4(vertexColor, 1.0);
+}
diff --git a/src/bin/phosphor_test.rs b/src/bin/phosphor_test.rs
new file mode 100644
index 0000000..40a8558
--- /dev/null
+++ b/src/bin/phosphor_test.rs
@@ -0,0 +1,89 @@
+#![allow(dead_code)]
+
+// use asbestos::{Shader, ShaderProgram};
+use buffer::*;
+use phosphor::*;
+// use raw_gl_context::GlContext;
+
+fn main() {
+ let my_program = MyProgram {};
+ let mut wm = WindowManager::with_program(my_program);
+ wm.create_window(MainWindow::new());
+ wm.run()
+}
+
+struct MyProgram {}
+impl ProgramController for MyProgram {}
+
+struct MainWindow {
+ // program: ShaderProgram,
+}
+impl MainWindow {
+ pub fn new() -> Box<Self> {
+ // let vertex_shader = Shader::vertex(include_str!("vertex.vert"));
+ // let fragment_shader = Shader::fragment(include_str!("fragment.frag"));
+ // let program = ShaderProgram::from_shaders(&[vertex_shader, fragment_shader]);
+ // Box::new(Self { program })
+ Box::new(Self {})
+ }
+}
+impl WindowController for MainWindow {
+ fn render(&mut self, buffer: &mut Buffer, _: RenderHint) {
+ println!("Rendering...");
+ buffer.fill(Colour::TEAL);
+ }
+
+ // fn render_gl(&mut self, _context: &mut GlContext) {
+ // println!("Rendering GL...");
+ // unsafe {
+ // gl::ClearColor(1.0, 0.0, 1.0, 1.0);
+ // gl::Clear(gl::COLOR_BUFFER_BIT);
+ // }
+ // }
+}
+
+// type Pos = [f32; 2];
+// type Color = [f32; 3];
+// #[repr(C, packed)]
+// struct Vertex(Pos, Color);
+
+// const VERTICES: [Vertex; 3] = [
+// Vertex([-0.5, -0.5], [1.0, 0.0, 0.0]),
+// Vertex([0.5, -0.5], [0.0, 1.0, 0.0]),
+// Vertex([0.0, 0.5], [0.0, 0.0, 1.0])
+// ];
+
+// pub struct Buffer {
+// pub id: GLuint,
+// target: GLuint,
+// }
+// impl Buffer {
+// pub unsafe fn new(target: GLuint) -> Self {
+// let mut id: GLuint = 0;
+// gl::GenBuffers(1, &mut id);
+// Self { id, target }
+// }
+
+// pub unsafe fn bind(&self) {
+// gl::BindBuffer(self.target, self.id);
+// }
+
+// pub unsafe fn set_data<D>(&self, data: &[D], usage: GLuint) {
+// self.bind();
+// let (_, data_bytes, _) = data.align_to::<u8>();
+// gl::BufferData(
+// self.target,
+// data_bytes.len() as GLsizeiptr,
+// data_bytes.as_ptr() as *const _,
+// usage,
+// );
+// }
+// }
+
+// impl Drop for Buffer {
+// fn drop(&mut self) {
+// unsafe {
+// gl::DeleteBuffers(1, [self.id].as_ptr());
+// }
+// }
+// }
diff --git a/src/bin/vertex.vert b/src/bin/vertex.vert
new file mode 100644
index 0000000..0fd4f8a
--- /dev/null
+++ b/src/bin/vertex.vert
@@ -0,0 +1,9 @@
+#version 330
+in vec2 point;
+in vec3 color;
+out vec3 vertexColor;
+
+void main() {
+ gl_Point = vec4(point, 0.0, 1.0);
+ vertexColor = color;
+}
diff --git a/src/keyboard_input.rs b/src/keyboard_input.rs
new file mode 100644
index 0000000..139db7e
--- /dev/null
+++ b/src/keyboard_input.rs
@@ -0,0 +1,19 @@
+use crate::*;
+use winit::event::KeyboardInput as WinitKeyboardInput;
+
+#[derive(Copy, Clone)]
+pub struct KeyboardInput {
+ pub state: PressState,
+ pub keycode: Option<KeyCode>,
+ pub scancode: u32,
+}
+
+impl From<WinitKeyboardInput> for KeyboardInput {
+ fn from(input: WinitKeyboardInput) -> Self {
+ Self {
+ state: input.state.into(),
+ keycode: input.virtual_keycode,
+ scancode: input.scancode,
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..75aaa1b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,29 @@
+mod keyboard_input;
+mod press_state;
+mod program_controller;
+mod render_hint;
+mod render_request;
+mod window;
+mod window_controller;
+mod window_manager;
+
+use window::Window;
+
+pub use keyboard_input::KeyboardInput;
+pub use press_state::PressState;
+pub use program_controller::{DefaultProgramController, ProgramController};
+pub use render_hint::RenderHint;
+pub use render_request::RenderRequest;
+pub use window_controller::WindowController;
+pub use window_manager::WindowManager;
+
+pub use buffer::{Buffer, Colour};
+pub use winit::{
+ event::ModifiersState,
+ event::VirtualKeyCode as KeyCode,
+ window::CursorIcon,
+};
+
+pub type Point = geometry::Point<i32>;
+pub type Dimensions = geometry::Dimensions<u32>;
+pub type Rect = geometry::Rect<i32, u32>;
diff --git a/src/press_state.rs b/src/press_state.rs
new file mode 100644
index 0000000..5136d66
--- /dev/null
+++ b/src/press_state.rs
@@ -0,0 +1,26 @@
+use winit::event::ElementState;
+
+/// Denotes whether an event was a press event or a release event.
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum PressState {
+ Pressed,
+ Released,
+}
+
+impl PressState {
+ pub fn is_pressed(&self) -> bool {
+ *self == Self::Pressed
+ }
+ pub fn is_released(&self) -> bool {
+ *self == Self::Released
+ }
+}
+
+impl From<ElementState> for PressState {
+ fn from(value: ElementState) -> Self {
+ match value {
+ ElementState::Pressed => PressState::Pressed,
+ ElementState::Released => PressState::Released,
+ }
+ }
+}
diff --git a/src/program_controller.rs b/src/program_controller.rs
new file mode 100644
index 0000000..638e5ce
--- /dev/null
+++ b/src/program_controller.rs
@@ -0,0 +1,13 @@
+use crate::*;
+
+pub trait ProgramController {
+ fn initialise(&mut self) {}
+ fn on_render(&mut self) {}
+ fn on_mouse_moved(&mut self, _position: Point) {}
+
+ fn on_process(&mut self, _create_window: &mut dyn FnMut(Box<dyn WindowController>)) {}
+}
+
+/// An empty program controller, for when a program has only one window.
+pub struct DefaultProgramController {}
+impl ProgramController for DefaultProgramController {}
diff --git a/src/render_hint.rs b/src/render_hint.rs
new file mode 100644
index 0000000..e4d37c3
--- /dev/null
+++ b/src/render_hint.rs
@@ -0,0 +1,33 @@
+use std::ops::*;
+
+/// A hint to tell a window controller whether the previous render state is
+/// intact and can be updated, or was destroyed and needs to be fully redrawn.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum RenderHint {
+ /// The buffer contents are intact, only updates need to be drawn.
+ Update,
+ /// The buffer contents were destroyed, a full redraw is required.
+ Redraw,
+}
+
+impl RenderHint {
+ pub fn is_update(&self) -> bool { *self == RenderHint::Update }
+ pub fn is_redraw(&self) -> bool { *self == RenderHint::Redraw }
+}
+
+impl BitAnd for RenderHint {
+ type Output = RenderHint;
+ fn bitand(self, other: RenderHint) -> Self::Output {
+ if self == RenderHint::Redraw || other == RenderHint::Redraw {
+ RenderHint::Redraw
+ } else {
+ RenderHint::Update
+ }
+ }
+}
+
+impl BitAndAssign for RenderHint {
+ fn bitand_assign(&mut self, other: RenderHint) {
+ *self = *self & other;
+ }
+}
diff --git a/src/render_request.rs b/src/render_request.rs
new file mode 100644
index 0000000..f336dfc
--- /dev/null
+++ b/src/render_request.rs
@@ -0,0 +1,40 @@
+use crate::*;
+use std::ops::*;
+
+/// A request to the window manager for a render pass to be run.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum RenderRequest {
+ /// A render is not required.
+ None,
+ /// A render is required.
+ Render(RenderHint),
+}
+
+impl RenderRequest {
+ pub const NONE: RenderRequest = RenderRequest::None;
+ pub const UPDATE: RenderRequest = RenderRequest::Render(RenderHint::Update);
+ pub const REDRAW: RenderRequest = RenderRequest::Render(RenderHint::Redraw);
+
+ pub fn is_none(&self) -> bool { *self == RenderRequest::NONE }
+ pub fn is_some(&self) -> bool { *self != RenderRequest::None }
+ pub fn is_update(&self) -> bool { *self == RenderRequest::UPDATE }
+ pub fn is_redraw(&self) -> bool { *self == RenderRequest::REDRAW }
+}
+
+impl BitAnd for RenderRequest {
+ type Output = RenderRequest;
+ fn bitand(self, other: RenderRequest) -> Self::Output {
+ use RenderRequest::*;
+ match (self, other) {
+ (None, req) => req,
+ (req, None) => req,
+ (Render(a), Render(b)) => Render(a & b),
+ }
+ }
+}
+
+impl BitAndAssign for RenderRequest {
+ fn bitand_assign(&mut self, other: RenderRequest) {
+ *self = *self & other;
+ }
+}
diff --git a/src/window.rs b/src/window.rs
new file mode 100644
index 0000000..45e37c9
--- /dev/null
+++ b/src/window.rs
@@ -0,0 +1,136 @@
+mod x11;
+use crate::*;
+use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
+use winit::dpi::{Size, PhysicalSize};
+use winit::event_loop::EventLoopWindowTarget;
+use winit::window::{WindowId, Window as WinitWindow, WindowBuilder as WinitWindowBuilder};
+// use raw_gl_context::{GlConfig, GlContext};
+
+pub struct Window {
+ pub controller: Box<dyn WindowController>,
+ cursor_position: Option<Point>,
+ winit_window: WinitWindow,
+ buffer: Buffer,
+ dimensions: Dimensions,
+ /// The most recent render request for this window.
+ render_hint: RenderHint,
+ graphics_context: Box<dyn GraphicsContext>,
+ // gl_context: GlContext,
+}
+
+impl Window {
+ pub unsafe fn new(event_loop: &EventLoopWindowTarget<()>, controller: Box<dyn WindowController>) -> Self {
+ let mut builder = WinitWindowBuilder::new();
+ builder = builder.with_resizable(controller.resizable());
+ builder = builder.with_inner_size({
+ let dim = controller.initial_dimensions();
+ Size::Physical(PhysicalSize::new(dim.width, dim.height))
+ });
+ if let Some(dim) = controller.minimum_dimensions() {
+ let size = Size::Physical(PhysicalSize { width: dim.width, height: dim.height });
+ builder = builder.with_min_inner_size(size);
+ }
+ if let Some(dim) = controller.maximum_dimensions() {
+ let size = Size::Physical(PhysicalSize { width: dim.width, height: dim.height });
+ builder = builder.with_max_inner_size(size);
+ }
+ let winit_window = builder.build(event_loop).unwrap();
+
+ let graphics_context: Box<dyn GraphicsContext> = match winit_window.raw_window_handle() {
+ RawWindowHandle::Xlib(xlib_handle) => Box::new(x11::X11GraphicsContext::new(xlib_handle)),
+ _ => panic!("Unknown window handle type"),
+ };
+ // let gl_context = GlContext::create(&winit_window, GlConfig::default()).unwrap();
+ // gl_context.make_current();
+ // gl::load_with(|symbol| {
+ // println!("Loaded '{}'", symbol);
+ // gl_context.get_proc_address(symbol) as *const _
+ // });
+ Self {
+ winit_window,
+ controller,
+ graphics_context,
+ render_hint: RenderHint::Redraw,
+ // gl_context,
+ buffer: Buffer::new(Dimensions::ZERO),
+ dimensions: Dimensions::ZERO,
+ cursor_position: None,
+ }
+ }
+
+ pub fn id(&self) -> WindowId {
+ self.winit_window.id()
+ }
+
+ pub fn set_minimum_dimensions(&mut self, dimensions: Option<Dimensions>) {
+ self.winit_window.set_min_inner_size(dimensions.map(|dim| {
+ Size::Physical(PhysicalSize { width:dim.width, height:dim.height })
+ }))
+ }
+ pub fn set_maximum_dimensions(&mut self, dimensions: Option<Dimensions>) {
+ self.winit_window.set_max_inner_size(dimensions.map(|dim| {
+ Size::Physical(PhysicalSize { width:dim.width, height:dim.height })
+ }))
+ }
+ pub fn set_title(&mut self, title: &str) {
+ self.winit_window.set_title(title);
+ }
+
+ /// Call to update the frame buffer to the new size of the window.
+ pub fn resize_buffer(&mut self, dimensions: Dimensions) {
+ if self.dimensions == dimensions { return }
+ self.dimensions = dimensions;
+ self.buffer.resize(dimensions);
+ self.controller.on_resize(dimensions);
+ self.render_hint = RenderHint::Redraw;
+ }
+
+ pub fn set_cursor_icon(&mut self, icon: Option<CursorIcon>) {
+ match icon {
+ Some(icon) => self.winit_window.set_cursor_icon(icon),
+ None => self.winit_window.set_cursor_icon(CursorIcon::Default),
+ };
+ }
+
+ /// Call this after a mouse click so that the cursor-hovering callbacks are
+ /// rerun. This is useful where a click changes the UI layout and a new
+ /// element that has an on-hover effect appears beneath the cursor.
+ pub fn bump_mouse(&mut self) {
+ if let Some(position) = self.cursor_position {
+ self.controller.on_mouse_move(position)
+ }
+ }
+
+ pub fn move_mouse(&mut self, position: Point) {
+ if self.cursor_position != Some(position) {
+ self.cursor_position = Some(position);
+ self.controller.on_mouse_move(position);
+ }
+ }
+
+ pub fn check_render_request(&mut self) {
+ if let RenderRequest::Render(hint) = self.controller.render_request() {
+ self.render_hint &= hint;
+ self.winit_window.request_redraw();
+ }
+ }
+
+ pub fn render(&mut self) {
+ self.controller.render(&mut self.buffer, self.render_hint);
+ unsafe { self.graphics_context.blit(&self.buffer); }
+ // Reset the render_hint back to the lowest variant.
+ self.render_hint = RenderHint::Update;
+ }
+
+ // pub fn render_gl(&mut self) {
+ // self.gl_context.make_current();
+ // self.controller.render_gl(&mut self.gl_context);
+ // self.gl_context.swap_buffers();
+ // self.gl_context.make_not_current();
+ // }
+}
+
+trait GraphicsContext {
+ /// Fill the graphics context with the contents of the provided buffer.
+ unsafe fn blit(&mut self, buffer: &Buffer);
+}
diff --git a/src/window/x11.rs b/src/window/x11.rs
new file mode 100644
index 0000000..d4bcbe4
--- /dev/null
+++ b/src/window/x11.rs
@@ -0,0 +1,66 @@
+use crate::window::GraphicsContext;
+use buffer::Buffer;
+use geometry::HasDimensions;
+use raw_window_handle::XlibHandle;
+use std::os::raw::{c_char, c_uint};
+use x11_dl::xlib::{Display, Visual, Xlib, ZPixmap, GC};
+
+pub struct X11GraphicsContext {
+ handle: XlibHandle,
+ lib: Xlib,
+ gc: GC,
+ visual: *mut Visual,
+ depth: i32,
+}
+
+impl X11GraphicsContext {
+ pub unsafe fn new(handle: XlibHandle) -> Self {
+ let lib = match Xlib::open() {
+ Ok(lib) => lib,
+ Err(e) => panic!("{:?}", e),
+ };
+ let screen = (lib.XDefaultScreen)(handle.display as *mut Display);
+ let gc = (lib.XDefaultGC)(handle.display as *mut Display, screen);
+ let visual = (lib.XDefaultVisual)(handle.display as *mut Display, screen);
+ let depth = (lib.XDefaultDepth)(handle.display as *mut Display, screen);
+
+ Self { handle, lib, gc, visual, depth }
+ }
+}
+
+impl GraphicsContext for X11GraphicsContext {
+ unsafe fn blit(&mut self, buffer: &Buffer) {
+ let array = buffer.as_u32_slice();
+ let dimensions = buffer.dimensions();
+ //create image
+ let image = (self.lib.XCreateImage)(
+ self.handle.display as *mut Display,
+ self.visual,
+ self.depth as u32,
+ ZPixmap,
+ 0,
+ (array.as_ptr()) as *mut c_char,
+ dimensions.width as u32,
+ dimensions.height as u32,
+ 32,
+ (dimensions.width * 4) as i32,
+ );
+
+ //push image to window
+ (self.lib.XPutImage)(
+ self.handle.display as *mut Display,
+ self.handle.window,
+ self.gc,
+ image,
+ 0,
+ 0,
+ 0,
+ 0,
+ dimensions.width as c_uint,
+ dimensions.height as c_uint,
+ );
+
+ (*image).data = std::ptr::null_mut();
+ (self.lib.XDestroyImage)(image);
+ }
+}
diff --git a/src/window_controller.rs b/src/window_controller.rs
new file mode 100644
index 0000000..d088b99
--- /dev/null
+++ b/src/window_controller.rs
@@ -0,0 +1,42 @@
+use crate::*;
+// use raw_gl_context::GlContext;
+
+/// Controls a single window.
+pub trait WindowController {
+ fn title(&self) -> String { String::from("Phosphor") }
+ fn initial_dimensions(&self) -> Dimensions { Dimensions::new(800,600) }
+ fn minimum_dimensions(&self) -> Option<Dimensions> { None }
+ fn maximum_dimensions(&self) -> Option<Dimensions> { None }
+ fn resizable(&self) -> bool { true }
+
+ fn cursor_icon(&mut self) -> Option<CursorIcon> { None }
+ fn cursor_visible(&mut self) -> bool { true }
+
+ fn on_resize(&mut self, _dimensions: Dimensions) {}
+ fn on_move(&mut self, _position: Point) {}
+ fn on_focus_change(&mut self, _focused: bool) {}
+ fn on_process(&mut self) {}
+
+ fn on_mouse_enter(&mut self) {}
+ fn on_mouse_exit(&mut self) {}
+ fn on_mouse_move(&mut self, _position: Point) {}
+
+ fn on_left_mouse_button(&mut self, _pressed: PressState) {}
+ fn on_middle_mouse_button(&mut self, _pressed: PressState) {}
+ fn on_right_mouse_button(&mut self, _pressed: PressState) {}
+
+ fn on_keyboard_input(&mut self, _input: KeyboardInput) {}
+ fn on_keyboard_modifier_change(&mut self, _modifiers: ModifiersState) {}
+ fn on_character_received(&mut self, _character: char) {}
+ fn on_file_hovered(&mut self, _path: std::path::PathBuf) {}
+ fn on_file_dropped(&mut self, _path: std::path::PathBuf) {}
+
+ fn on_line_scroll_horizontal(&mut self, _delta: f64) {}
+ fn on_line_scroll_vertical(&mut self, _delta: f64) {}
+ fn on_pixel_scroll_horizontal(&mut self, _delta: f64) {}
+ fn on_pixel_scroll_vertical(&mut self, _delta: f64) {}
+
+ fn render_request(&mut self) -> RenderRequest { RenderRequest::None }
+ fn render(&mut self, _buffer: &mut Buffer, _hint: RenderHint) {}
+ // fn render_gl(&mut self, _context: &mut GlContext) {}
+}
diff --git a/src/window_manager.rs b/src/window_manager.rs
new file mode 100644
index 0000000..144f1c3
--- /dev/null
+++ b/src/window_manager.rs
@@ -0,0 +1,156 @@
+use crate::*;
+use nosferatu::*;
+
+use std::collections::HashMap;
+use winit::event::{Event, MouseButton, StartCause, WindowEvent, MouseScrollDelta};
+use winit::event_loop::EventLoop;
+use winit::window::WindowId;
+
+/// Controls the entire program.
+pub struct WindowManager<P: ProgramController> {
+ limiter: Option<FrameRateLimiter>,
+ frame_timer: FrameTimer,
+ event_loop: EventLoop<()>,
+ windows: HashMap<WindowId, Window>,
+ program: P,
+}
+
+impl WindowManager<DefaultProgramController> {
+ pub fn without_program() -> Self {
+ Self::with_program(DefaultProgramController {})
+ }
+}
+
+impl<P: 'static + ProgramController> WindowManager<P> {
+ pub fn with_program(program: P) -> Self {
+ Self {
+ limiter: None,
+ frame_timer: FrameTimer::one_second(),
+ event_loop: EventLoop::new(),
+ windows: HashMap::new(),
+ program,
+ }
+ }
+
+ pub fn with_frame_limit(mut self, limit: usize) -> Self {
+ self.limiter = Some(FrameRateLimiter::from_frame_rate(limit)); self
+ }
+
+ /// Used to create one or more windows before the event loop starts.
+ pub fn create_window(&mut self, controller: Box<dyn WindowController>) {
+ let window = unsafe { Window::new(&self.event_loop, controller) };
+ self.windows.insert(window.id(), window);
+ }
+
+ /// Starts the event loop, causing program control to be passed permanently to the window manager.
+ pub fn run(mut self) -> ! {
+ self.event_loop.run(move |event, window_target, control_flow| {
+ control_flow.set_poll();
+
+ match event {
+ // Event loop has just initialised (is only ever emitted once)
+ Event::NewEvents(StartCause::Init) => self.program.initialise(),
+
+ Event::WindowEvent { window_id, event } => {
+ if let Some(window) = self.windows.get_mut(&window_id) {
+ use WindowEvent::*;
+ match event {
+ Resized(dim) => window.resize_buffer(Dimensions::new(dim.width, dim.height)),
+ Moved(p) => window.controller.on_move(Point::new(p.x, p.y)),
+ Focused(state) => window.controller.on_focus_change(state),
+
+ CursorEntered { .. } => window.controller.on_mouse_enter(),
+ CursorLeft { .. } => window.controller.on_mouse_exit(),
+ CursorMoved { position, .. } => {
+ let point = Point::new(position.x as i32, position.y as i32);
+ window.move_mouse(point);
+ }
+ MouseWheel {delta, ..} => {
+ match delta {
+ MouseScrollDelta::LineDelta(x, y) => {
+ let (x, y) = (x as f64, y as f64);
+ if x != 0.0 {window.controller.on_line_scroll_horizontal(x)}
+ if y != 0.0 {window.controller.on_line_scroll_vertical(y)}
+ }
+ MouseScrollDelta::PixelDelta(point) => {
+ let (x, y) = (point.x, point.y);
+ if x != 0.0 {window.controller.on_pixel_scroll_horizontal(x)}
+ if y != 0.0 {window.controller.on_pixel_scroll_vertical(y)}
+ }
+ }
+ },
+ MouseInput { state, button, .. } => {
+ match button {
+ MouseButton::Left => window.controller.on_left_mouse_button(state.into()),
+ MouseButton::Middle => window.controller.on_middle_mouse_button(state.into()),
+ MouseButton::Right => window.controller.on_right_mouse_button(state.into()),
+ _ => (),
+ }
+ window.bump_mouse();
+
+ }
+
+ KeyboardInput { input, .. } => window.controller.on_keyboard_input(input.into()),
+ ModifiersChanged(state) => window.controller.on_keyboard_modifier_change(state),
+ ReceivedCharacter(c) => window.controller.on_character_received(c),
+ HoveredFile(path) => window.controller.on_file_hovered(path),
+ DroppedFile(path) => window.controller.on_file_dropped(path),
+
+ // Tell the window, and let it raise its own event to close itself
+ // When all windows are closed: control_flow.set_exit_with_code(0);
+ CloseRequested => todo!("window.controller.on_close_requested()"),
+ _ => (),
+ }
+ }
+ }
+
+ // Called each loop before any rendering would begin.
+ Event::MainEventsCleared => {
+ self.frame_timer.tick();
+ // println!("{}", self.frame_timer.frame_rate());
+
+ self.program.on_process(&mut |controller: Box<dyn WindowController>| {
+ let window = unsafe { Window::new(window_target, controller) };
+ self.windows.insert(window.id(), window);
+ });
+ for window in self.windows.values_mut() {
+ window.controller.on_process();
+ let cursor_icon = window.controller.cursor_icon();
+ window.set_cursor_icon(cursor_icon);
+ // let resizable = window.controller.resizable();
+ // window.set_resizable(resizable);
+ // let cursor_visible = window.controller.cursor_visible();
+ // window.set_cursor_visible(cursor_visible);
+ window.check_render_request();
+ let title = window.controller.title();
+ window.set_title(&title);
+ let minimum_dimensions = window.controller.minimum_dimensions();
+ window.set_minimum_dimensions(minimum_dimensions);
+ let maximum_dimensions = window.controller.maximum_dimensions();
+ window.set_maximum_dimensions(maximum_dimensions);
+ }
+ }
+ // Called if a render was requested for a window
+ Event::RedrawRequested(window_id) => {
+ if let Some(window) = self.windows.get_mut(&window_id) {
+ window.render();
+ }
+ }
+ // Called after rendering has completed
+ Event::RedrawEventsCleared => {
+ if let Some(limiter) = &mut self.limiter {
+ limiter.tick();
+ }
+ }
+
+ _other => {} // todo!("{:?}", other),
+ }
+ })
+ }
+}
+
+impl Default for WindowManager<DefaultProgramController> {
+ fn default() -> Self {
+ Self::without_program()
+ }
+}