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.rs97
-rw-r--r--src/bin/vertex.vert9
-rw-r--r--src/keyboard_input.rs19
-rw-r--r--src/lib.rs60
-rw-r--r--src/press_state.rs26
-rw-r--r--src/program_controller.rs13
-rw-r--r--src/render.rs67
-rw-r--r--src/render_hint.rs33
-rw-r--r--src/render_request.rs40
-rw-r--r--src/window.rs177
-rw-r--r--src/window/x11.rs66
-rw-r--r--src/window_controller.rs56
-rw-r--r--src/window_manager.rs198
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,
- }
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 75aaa1b..b610486 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()
- }
-}