diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/fragment.frag | 7 | ||||
-rw-r--r-- | src/bin/phosphor_test.rs | 97 | ||||
-rw-r--r-- | src/bin/vertex.vert | 9 | ||||
-rw-r--r-- | src/keyboard_input.rs | 19 | ||||
-rw-r--r-- | src/lib.rs | 60 | ||||
-rw-r--r-- | src/press_state.rs | 26 | ||||
-rw-r--r-- | src/program_controller.rs | 13 | ||||
-rw-r--r-- | src/render.rs | 67 | ||||
-rw-r--r-- | src/render_hint.rs | 33 | ||||
-rw-r--r-- | src/render_request.rs | 40 | ||||
-rw-r--r-- | src/window.rs | 177 | ||||
-rw-r--r-- | src/window/x11.rs | 66 | ||||
-rw-r--r-- | src/window_controller.rs | 56 | ||||
-rw-r--r-- | src/window_manager.rs | 198 |
14 files changed, 338 insertions, 530 deletions
diff --git a/src/bin/fragment.frag b/src/bin/fragment.frag deleted file mode 100644 index 99826f6..0000000 --- a/src/bin/fragment.frag +++ /dev/null @@ -1,7 +0,0 @@ -#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 index 40a8558..c3b307d 100644 --- a/src/bin/phosphor_test.rs +++ b/src/bin/phosphor_test.rs @@ -1,89 +1,36 @@ -#![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()); + let mut wm = WindowManager::new(std::time::Duration::from_micros(16666)); + wm.add_window(Box::new(Window {})); 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 {}) +struct Window {} +impl WindowController for Window { + fn minimum_size(&self) -> Option<phosphor::Dimensions> { + Some(phosphor::Dimensions::new(200, 200)) } -} -impl WindowController for MainWindow { - fn render(&mut self, buffer: &mut Buffer, _: RenderHint) { - println!("Rendering..."); - buffer.fill(Colour::TEAL); + fn maximum_size(&self) -> Option<phosphor::Dimensions> { + Some(phosphor::Dimensions::new(400, 400)) } - // 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]) -// ]; + fn render_request(&self) -> RenderRequest { + RenderRequest::None + } -// 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 } -// } + fn is_resizable(&self) -> bool { + false + } -// pub unsafe fn bind(&self) { -// gl::BindBuffer(self.target, self.id); -// } + fn on_resize(&mut self, size: phosphor::Dimensions) { + println!("RESIZE: {size:?}"); + } -// 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, -// ); -// } -// } + fn on_render(&mut self, buffer: &mut Buffer, _: RenderHint) { + println!("RENDER"); + buffer.fill(Colour::TEAL); + } +} -// 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 deleted file mode 100644 index 0fd4f8a..0000000 --- a/src/bin/vertex.vert +++ /dev/null @@ -1,9 +0,0 @@ -#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 deleted file mode 100644 index 139db7e..0000000 --- a/src/keyboard_input.rs +++ /dev/null @@ -1,19 +0,0 @@ -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, - } - } -} @@ -1,25 +1,16 @@ -mod keyboard_input; -mod press_state; -mod program_controller; -mod render_hint; -mod render_request; +mod render; 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 render::*; +pub use window::*; +pub use window_controller::*; +pub use window_manager::*; pub use buffer::{Buffer, Colour}; pub use winit::{ - event::ModifiersState, + event::{ModifiersState, ElementState}, event::VirtualKeyCode as KeyCode, window::CursorIcon, }; @@ -27,3 +18,42 @@ pub use winit::{ pub type Point = geometry::Point<i32>; pub type Dimensions = geometry::Dimensions<u32>; pub type Rect = geometry::Rect<i32, u32>; + +// ----------------------------------------------------------------------------- + +#[derive(Copy, Clone)] +pub struct KeyboardInput { + pub action: Action, + pub key: KeyCode, +} + +impl TryFrom<winit::event::KeyboardInput> for KeyboardInput { + type Error = (); + + fn try_from(input: winit::event::KeyboardInput) -> Result<Self, ()> { + if let Some(key) = input.virtual_keycode { + Ok( Self { action: input.state.into(), key } ) + } else { + Err(()) + } + } +} + +// ----------------------------------------------------------------------------- + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Action { Pressed, Released } + +impl Action { + pub fn is_pressed(&self) -> bool { *self == Self::Pressed } + pub fn is_released(&self) -> bool { *self == Self::Released } +} + +impl From<ElementState> for Action { + fn from(value: ElementState) -> Self { + match value { + ElementState::Pressed => Action::Pressed, + ElementState::Released => Action::Released, + } + } +} diff --git a/src/press_state.rs b/src/press_state.rs deleted file mode 100644 index 5136d66..0000000 --- a/src/press_state.rs +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 638e5ce..0000000 --- a/src/program_controller.rs +++ /dev/null @@ -1,13 +0,0 @@ -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.rs b/src/render.rs new file mode 100644 index 0000000..e7ec02d --- /dev/null +++ b/src/render.rs @@ -0,0 +1,67 @@ +/// Tells 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 content is unchanged, only updates are required. + Update, + /// The buffer content has been 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 std::ops::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 std::ops::BitAndAssign for RenderHint { + fn bitand_assign(&mut self, other: RenderHint) { + *self = *self & other; + } +} + +/// A request to the window manager for a render pass to be run. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RenderRequest { + None, + 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 std::ops::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 std::ops::BitAndAssign for RenderRequest { + fn bitand_assign(&mut self, other: RenderRequest) { + *self = *self & other; + } +} diff --git a/src/render_hint.rs b/src/render_hint.rs deleted file mode 100644 index e4d37c3..0000000 --- a/src/render_hint.rs +++ /dev/null @@ -1,33 +0,0 @@ -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 deleted file mode 100644 index f336dfc..0000000 --- a/src/render_request.rs +++ /dev/null @@ -1,40 +0,0 @@ -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 index 45e37c9..ffe35e8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,136 +1,133 @@ -mod x11; use crate::*; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + +use std::num::NonZeroU32; 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}; +use winit::window::WindowId; pub struct Window { pub controller: Box<dyn WindowController>, - cursor_position: Option<Point>, - winit_window: WinitWindow, + window: winit::window::Window, buffer: Buffer, dimensions: Dimensions, - /// The most recent render request for this window. - render_hint: RenderHint, - graphics_context: Box<dyn GraphicsContext>, - // gl_context: GlContext, + #[allow(dead_code)] context: softbuffer::Context, + surface: softbuffer::Surface, + current_render_hint: RenderHint, + previous_cursor_position: Option<Point>, } 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(); + pub fn new(event_loop: &EventLoopWindowTarget<()>, controller: Box<dyn WindowController>) -> Self { + let window = winit::window::WindowBuilder::new() + .with_title(controller.title()) + .with_resizable(controller.is_resizable()) + .with_inner_size(dim_to_size(controller.initial_size())) + .build(event_loop) + .unwrap(); + + let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); + let surface = unsafe { softbuffer::Surface::new(&context, &window) }.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, + window, buffer: Buffer::new(Dimensions::ZERO), dimensions: Dimensions::ZERO, - cursor_position: None, + context, + surface, + previous_cursor_position: None, + current_render_hint: RenderHint::Redraw, } } pub fn id(&self) -> WindowId { - self.winit_window.id() + self.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 update_title(&mut self) { + self.window.set_title(&self.controller.title()); } - 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 update_cursor_icon(&mut self) { + let icon = self.controller.cursor_icon().unwrap_or(CursorIcon::Default); + self.window.set_cursor_icon(icon); } - pub fn set_title(&mut self, title: &str) { - self.winit_window.set_title(title); + + pub fn update_minimum_size(&mut self) { + let size = self.controller.minimum_size().map(|d| dim_to_size(d)); + self.window.set_min_inner_size(size); } - /// 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 update_maximum_size(&mut self) { + let size = self.controller.maximum_size().map(|d| dim_to_size(d)); + self.window.set_max_inner_size(size); } - 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), - }; + pub fn update_cursor_visible(&mut self) { + self.window.set_cursor_visible(self.controller.is_cursor_visible()); } - /// 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 update_resizable(&mut self) { + let is_resizable = self.controller.is_resizable(); + self.window.set_resizable(is_resizable); + // Hack to force window to be impossible to resize on DWM, where the + // 'is_resizable' window attribute isn't respected. + if !is_resizable { + self.window.set_min_inner_size(Some(self.window.inner_size())); + self.window.set_max_inner_size(Some(self.window.inner_size())); } } - 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); + /// Resize the frame buffer to be the new size of the window. + pub fn resize(&mut self, dimensions: Dimensions) { + if self.dimensions == dimensions { return } + self.dimensions = dimensions; + self.buffer.resize(dimensions); + if let Some((width, height)) = dim_to_nonzero_size(dimensions) { + self.surface.resize(width, height).unwrap(); + }; + self.controller.on_resize(dimensions); + self.current_render_hint = RenderHint::Redraw; + } + + pub fn move_cursor(&mut self, position: Point) { + // The cursor position is rounded to i32 from f64, so we need to ignore + // duplicate consecutive cursor positions. + if self.previous_cursor_position != Some(position) { + self.previous_cursor_position = Some(position); + self.controller.on_cursor_move(position); } } - pub fn check_render_request(&mut self) { + pub fn handle_render_request(&mut self) { if let RenderRequest::Render(hint) = self.controller.render_request() { - self.render_hint &= hint; - self.winit_window.request_redraw(); + self.current_render_hint &= hint; + self.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; + let size = self.window.inner_size(); + let dim = Dimensions::new(size.width, size.height); + self.resize(dim); + self.controller.on_render(&mut self.buffer, self.current_render_hint); + let buffer = self.buffer.as_u32_slice(); + let mut surface = self.surface.buffer_mut().unwrap(); + let buffer_len = buffer.len(); + let surface_len = surface.len(); + surface[..buffer_len].copy_from_slice(&buffer[..surface_len]); + surface.present().unwrap(); + // Reset current_render_hint back to the lowest variant for the next frame. + self.current_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(); - // } +fn dim_to_size(dimensions: Dimensions) -> Size { + Size::Physical( PhysicalSize { width:dimensions.width, height:dimensions.height }) } -trait GraphicsContext { - /// Fill the graphics context with the contents of the provided buffer. - unsafe fn blit(&mut self, buffer: &Buffer); +fn dim_to_nonzero_size(dimensions: Dimensions) -> Option<(NonZeroU32, NonZeroU32)> { + let width = NonZeroU32::new(dimensions.width)?; + let height = NonZeroU32::new(dimensions.height)?; + return Some((width, height)); } diff --git a/src/window/x11.rs b/src/window/x11.rs deleted file mode 100644 index d4bcbe4..0000000 --- a/src/window/x11.rs +++ /dev/null @@ -1,66 +0,0 @@ -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 index d088b99..34f9fd4 100644 --- a/src/window_controller.rs +++ b/src/window_controller.rs @@ -1,42 +1,44 @@ 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 cursor_icon(&self) -> Option<CursorIcon> { None } + fn initial_size(&self) -> Dimensions { Dimensions::new(800,600) } + fn minimum_size(&self) -> Option<Dimensions> { None } + fn maximum_size(&self) -> Option<Dimensions> { None } + fn pixel_scale(&self) -> u32 { 1 } + fn render_request(&self) -> RenderRequest { RenderRequest::None } + + fn is_visible(&self) -> bool { true } + fn is_cursor_visible(&self) -> bool { true } + fn is_resizable(&self) -> bool { true } + + fn on_init(&mut self) {} + fn on_resize(&mut self, _size: Dimensions) {} fn on_move(&mut self, _position: Point) {} - fn on_focus_change(&mut self, _focused: bool) {} + fn on_focus_change(&mut self, _is_focused: bool) {} fn on_process(&mut self) {} + fn on_render(&mut self, _buffer: &mut Buffer, _hint: RenderHint) {} + fn on_close_request(&mut self) {} + fn on_close(&mut self) {} - fn on_mouse_enter(&mut self) {} - fn on_mouse_exit(&mut self) {} - fn on_mouse_move(&mut self, _position: Point) {} + fn on_cursor_enter(&mut self) {} + fn on_cursor_exit(&mut self) {} + fn on_cursor_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_left_mouse_button(&mut self, _action: Action) {} + fn on_middle_mouse_button(&mut self, _action: Action) {} + fn on_right_mouse_button(&mut self, _action: Action) {} 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) {} + fn on_keyboard_input(&mut self, _input: KeyboardInput) {} + fn on_keyboard_modifier_change(&mut self, _modifiers: ModifiersState) {} + fn on_character_input(&mut self, _character: char) {} + fn on_file_hover(&mut self, _path: std::path::PathBuf) {} + fn on_file_hover_cancel(&mut self) {} + fn on_file_drop(&mut self, _path: std::path::PathBuf) {} } diff --git a/src/window_manager.rs b/src/window_manager.rs index 144f1c3..065d517 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -1,156 +1,134 @@ 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, +use std::time::Duration; +use winit::{event::*, event_loop::{EventLoop, ControlFlow}, window::WindowId}; + +pub struct WindowManager { event_loop: EventLoop<()>, windows: HashMap<WindowId, Window>, - program: P, + min_frame_duration: Duration, } -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 { +impl WindowManager { + pub fn new(min_frame_duration: Duration) -> Self { Self { - limiter: None, - frame_timer: FrameTimer::one_second(), event_loop: EventLoop::new(), windows: HashMap::new(), - program, + min_frame_duration, } } - 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) }; + /// Add a window to the window manager before the event loop begins. + pub fn add_window(&mut self, controller: Box<dyn WindowController>) { + let window = 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. + /// Start the event loop, passing program control to the window manager. pub fn run(mut self) -> ! { - self.event_loop.run(move |event, window_target, control_flow| { - control_flow.set_poll(); - + self.event_loop.run(move |event, _window_target, control_flow| { match event { - // Event loop has just initialised (is only ever emitted once) - Event::NewEvents(StartCause::Init) => self.program.initialise(), + // Called when the event loop is first initialized. + Event::NewEvents(StartCause::Init) => (), + + Event::NewEvents(_) => { + control_flow.set_wait_timeout(self.min_frame_duration); + } + + // Called when an application suspends on a mobile platform. + Event::Suspended => (), + + // Called when an application resumes, or after initialization on non-mobile platforms. + Event::Resumed => (), 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)} - } + Resized(dim) => window.resize(Dimensions::new(dim.width, dim.height)), + Moved(position) => window.controller.on_move(Point::new(position.x, position.y)), + Focused(is_focused) => window.controller.on_focus_change(is_focused), + + CursorEntered { .. } => window.controller.on_cursor_enter(), + CursorLeft { .. } => window.controller.on_cursor_exit(), + CursorMoved { position: p, .. } => { window.move_cursor(Point::new(p.x as i32, p.y as i32)) } + + 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)} } - }, - 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()), - _ => (), + 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)} } - window.bump_mouse(); - + } + 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()), + MouseButton::Other(_) => (), } - KeyboardInput { input, .. } => window.controller.on_keyboard_input(input.into()), + KeyboardInput { input, .. } => if let Ok(input) = input.try_into() { window.controller.on_keyboard_input(input)}, 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()"), - _ => (), + ReceivedCharacter(character) => window.controller.on_character_input(character), + HoveredFile(path) => window.controller.on_file_hover(path), + HoveredFileCancelled => window.controller.on_file_hover_cancel(), + DroppedFile(path) => window.controller.on_file_drop(path), + + CloseRequested => { + window.controller.on_close_request(); + *control_flow = ControlFlow::Exit; + }, + Destroyed => window.controller.on_close(), + + Ime(_) => (), + AxisMotion { .. } => (), + Touch(_) => (), + TouchpadRotate { .. } => (), + TouchpadPressure { .. } => (), + TouchpadMagnify { .. } => (), + SmartMagnify { .. } => (), + ScaleFactorChanged { .. } => (), + ThemeChanged(_) => (), + Occluded(_) => (), } } } - // Called each loop before any rendering would begin. + // Called before any render events are called. 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); + window.update_title(); + window.update_minimum_size(); + window.update_maximum_size(); + window.update_resizable(); + window.update_cursor_icon(); + window.update_cursor_visible(); + window.handle_render_request(); } } - // Called if a render was requested for a window + + // Called if a window has requested to be rendered. 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), + // Called after all rendering has completed, or if there were no render requests. + Event::RedrawEventsCleared => (), + + // Called before the program closes. + Event::LoopDestroyed => (), + + _ => (), } }) } } - -impl Default for WindowManager<DefaultProgramController> { - fn default() -> Self { - Self::without_program() - } -} |