Added better error handling

This commit is contained in:
David Johnson 2022-01-16 08:59:29 -06:00
parent e4b1917333
commit dc6fc474be
8 changed files with 136 additions and 105 deletions

View file

@ -6,6 +6,7 @@ authors = ["David Johnson <john01dav@gmail.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
thiserror = "1.0.30"
raw-window-handle = "0.4.2" raw-window-handle = "0.4.2"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]

View file

@ -52,15 +52,15 @@ For now, the priority for new platforms is:
Example Example
== ==
```no_run ```no_run
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent}; use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder; use winit::window::WindowBuilder;
use softbuffer::GraphicsContext;
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut graphics_context = unsafe { GraphicsContext::new(window) }; let mut graphics_context = unsafe { GraphicsContext::new(window) }.unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
@ -71,26 +71,28 @@ fn main() {
let size = graphics_context.window().inner_size(); let size = graphics_context.window().inner_size();
(size.width, size.height) (size.width, size.height)
}; };
let buffer = (0..((width*height) as usize)).map(|index|{ let buffer = (0..((width * height) as usize))
let y = index / (width as usize); .map(|index| {
let x = index % (width as usize); let y = index / (width as usize);
let red = x % 255; let x = index % (width as usize);
let green = y % 255; let red = x % 255;
let blue = (x*y) % 255; let green = y % 255;
let blue = (x * y) % 255;
let color = blue | (green << 8) | (red << 16); let color = blue | (green << 8) | (red << 16);
color as u32 color as u32
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, width as u16, height as u16); graphics_context.set_buffer(&buffer, width as u16, height as u16);
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id window_id,
} if window_id == graphics_context.window().id() => { } if window_id == graphics_context.window().id() => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
}, }
_ => {} _ => {}
} }
}); });

View file

@ -1,12 +1,12 @@
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent}; use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder; use winit::window::WindowBuilder;
use softbuffer::GraphicsContext;
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut graphics_context = unsafe { GraphicsContext::new(window) }; let mut graphics_context = unsafe { GraphicsContext::new(window) }.unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
@ -17,27 +17,29 @@ fn main() {
let size = graphics_context.window().inner_size(); let size = graphics_context.window().inner_size();
(size.width, size.height) (size.width, size.height)
}; };
let buffer = (0..((width*height) as usize)).map(|index|{ let buffer = (0..((width * height) as usize))
let y = index / (width as usize); .map(|index| {
let x = index % (width as usize); let y = index / (width as usize);
let red = x % 255; let x = index % (width as usize);
let green = y % 255; let red = x % 255;
let blue = (x*y) % 255; let green = y % 255;
let blue = (x * y) % 255;
let color = blue | (green << 8) | (red << 16); let color = blue | (green << 8) | (red << 16);
color as u32 color as u32
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, width as u16, height as u16); graphics_context.set_buffer(&buffer, width as u16, height as u16);
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id window_id,
} if window_id == graphics_context.window().id() => { } if window_id == graphics_context.window().id() => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
}, }
_ => {} _ => {}
} }
}); });
} }

View file

@ -1,7 +1,7 @@
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent}; use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder; use winit::window::WindowBuilder;
use softbuffer::GraphicsContext;
const BUFFER_WIDTH: usize = 256; const BUFFER_WIDTH: usize = 256;
const BUFFER_HEIGHT: usize = 128; const BUFFER_HEIGHT: usize = 128;
@ -9,34 +9,36 @@ const BUFFER_HEIGHT: usize = 128;
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut graphics_context = unsafe { GraphicsContext::new(window) }; let mut graphics_context = unsafe { GraphicsContext::new(window) }.unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
match event { match event {
Event::RedrawRequested(window_id) if window_id == graphics_context.window().id() => { Event::RedrawRequested(window_id) if window_id == graphics_context.window().id() => {
let buffer = (0..((BUFFER_WIDTH*BUFFER_HEIGHT) as usize)).map(|index|{ let buffer = (0..((BUFFER_WIDTH * BUFFER_HEIGHT) as usize))
let y = index / (BUFFER_WIDTH as usize); .map(|index| {
let x = index % (BUFFER_WIDTH as usize); let y = index / (BUFFER_WIDTH as usize);
let red = x % 255; let x = index % (BUFFER_WIDTH as usize);
let green = y % 255; let red = x % 255;
let blue = (x*y) % 255; let green = y % 255;
let blue = (x * y) % 255;
let color = blue | (green << 8) | (red << 16); let color = blue | (green << 8) | (red << 16);
color as u32 color as u32
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, BUFFER_WIDTH as u16, BUFFER_HEIGHT as u16); graphics_context.set_buffer(&buffer, BUFFER_WIDTH as u16, BUFFER_HEIGHT as u16);
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id window_id,
} if window_id == graphics_context.window().id() => { } if window_id == graphics_context.window().id() => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
}, }
_ => {} _ => {}
} }
}); });
} }

17
src/error.rs Normal file
View file

@ -0,0 +1,17 @@
use std::error::Error;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum SoftBufferError<W: HasRawWindowHandle> {
#[error(
"The provided window returned an unsupported platform: {human_readable_platform_name}."
)]
UnsupportedPlatform {
window: W,
human_readable_platform_name: &'static str,
handle: RawWindowHandle,
},
#[error("Platform error")]
PlatformError(Option<String>, Option<Box<dyn Error>>)
}

View file

@ -1,9 +1,12 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#[cfg(target_os = "linux")]
mod x11;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod win32; mod win32;
#[cfg(target_os = "linux")]
mod x11;
mod error;
pub use error::SoftBufferError;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
@ -11,43 +14,46 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
/// write to a window on that platform. This struct owns the window that this data corresponds to /// write to a window on that platform. This struct owns the window that this data corresponds to
/// to ensure safety, as that data must be destroyed before the window itself is destroyed. You may /// to ensure safety, as that data must be destroyed before the window itself is destroyed. You may
/// access the underlying window via [`window`](Self::window) and [`window_mut`](Self::window_mut). /// access the underlying window via [`window`](Self::window) and [`window_mut`](Self::window_mut).
pub struct GraphicsContext<W: HasRawWindowHandle>{ pub struct GraphicsContext<W: HasRawWindowHandle> {
window: W, window: W,
graphics_context_impl: Box<dyn GraphicsContextImpl> graphics_context_impl: Box<dyn GraphicsContextImpl>,
} }
impl<W: HasRawWindowHandle> GraphicsContext<W> { impl<W: HasRawWindowHandle> GraphicsContext<W> {
/// Creates a new instance of this struct, consuming the given window. /// Creates a new instance of this struct, consuming the given window.
/// ///
/// # Safety /// # Safety
/// ///
/// - Ensure that the passed object is valid to draw a 2D buffer to /// - Ensure that the passed object is valid to draw a 2D buffer to
pub unsafe fn new(window: W) -> Self{ pub unsafe fn new(window: W) -> Result<Self, SoftBufferError<W>> {
let raw_handle = window.raw_window_handle(); let raw_handle = window.raw_window_handle();
let imple = match raw_handle{ let imple = match raw_handle {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
RawWindowHandle::Xlib(xlib_handle) => Box::new(x11::X11Impl::new(xlib_handle)), RawWindowHandle::Xlib(xlib_handle) => Box::new(x11::X11Impl::new(xlib_handle)?),
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
RawWindowHandle::Win32(win32_handle) => Box::new(win32::Win32Impl::new(&win32_handle)), RawWindowHandle::Win32(win32_handle) => Box::new(win32::Win32Impl::new(&win32_handle)?),
unimplemented_handle_type => unimplemented!("Unsupported window handle type: {}.", window_handle_type_name(&unimplemented_handle_type)) unimplemented_handle_type => return Err(SoftBufferError::UnsupportedPlatform {
window,
human_readable_platform_name: window_handle_type_name(&unimplemented_handle_type),
handle: unimplemented_handle_type,
}),
}; };
Self{ Ok(Self {
window, window,
graphics_context_impl: imple graphics_context_impl: imple,
} })
} }
/// Gets shared access to the underlying window /// Gets shared access to the underlying window
#[inline] #[inline]
pub fn window(&self) -> &W{ pub fn window(&self) -> &W {
&self.window &self.window
} }
/// Gets mut/exclusive access to the underlying window /// Gets mut/exclusive access to the underlying window
#[inline] #[inline]
pub fn window_mut(&mut self) -> &mut W{ pub fn window_mut(&mut self) -> &mut W {
&mut self.window &mut self.window
} }
@ -75,8 +81,8 @@ impl<W: HasRawWindowHandle> GraphicsContext<W> {
/// G: Green channel /// G: Green channel
/// B: Blue channel /// B: Blue channel
#[inline] #[inline]
pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16){ pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
if (width as usize)*(height as usize) != buffer.len(){ if (width as usize) * (height as usize) != buffer.len() {
panic!("The size of the passed buffer is not the correct size. Its length must be exactly width*height."); panic!("The size of the passed buffer is not the correct size. Its length must be exactly width*height.");
} }
@ -84,15 +90,14 @@ impl<W: HasRawWindowHandle> GraphicsContext<W> {
self.graphics_context_impl.set_buffer(buffer, width, height); self.graphics_context_impl.set_buffer(buffer, width, height);
} }
} }
} }
trait GraphicsContextImpl{ trait GraphicsContextImpl {
unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16); unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16);
} }
fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str{ fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
match handle{ match handle {
RawWindowHandle::Xlib(_) => "Xlib", RawWindowHandle::Xlib(_) => "Xlib",
RawWindowHandle::Win32(_) => "Win32", RawWindowHandle::Win32(_) => "Win32",
RawWindowHandle::WinRt(_) => "WinRt", RawWindowHandle::WinRt(_) => "WinRt",
@ -102,6 +107,6 @@ fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str{
RawWindowHandle::AppKit(_) => "AppKit", RawWindowHandle::AppKit(_) => "AppKit",
RawWindowHandle::Orbital(_) => "Orbital", RawWindowHandle::Orbital(_) => "Orbital",
RawWindowHandle::UiKit(_) => "UiKit", RawWindowHandle::UiKit(_) => "UiKit",
_ => "Unknown Name" //don't completely fail to compile if there is a new raw window handle type that's added at some point _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
} }
} }

View file

@ -1,13 +1,13 @@
use crate::{GraphicsContextImpl, SoftBufferError};
use raw_window_handle::{HasRawWindowHandle, Win32Handle};
use std::os::raw::c_int; use std::os::raw::c_int;
use raw_window_handle::Win32Handle; use winapi::shared::windef::{HDC, HWND};
use crate::GraphicsContextImpl; use winapi::um::wingdi::{StretchDIBits, BITMAPINFOHEADER, BI_BITFIELDS, RGBQUAD};
use winapi::um::wingdi::{BITMAPINFOHEADER, BI_BITFIELDS, RGBQUAD, StretchDIBits}; use winapi::um::winuser::{GetDC, ValidateRect};
use winapi::um::winuser::{ValidateRect, GetDC};
use winapi::shared::windef::{HWND, HDC};
pub struct Win32Impl{ pub struct Win32Impl {
window: HWND, window: HWND,
dc: HDC dc: HDC,
} }
// Wrap this so we can have a proper number of bmiColors to write in // Wrap this so we can have a proper number of bmiColors to write in
@ -19,15 +19,20 @@ struct BitmapInfo {
} }
impl Win32Impl { impl Win32Impl {
pub unsafe fn new<W: HasRawWindowHandle>(handle: &Win32Handle) -> Result<Self, crate::SoftBufferError<W>> {
pub unsafe fn new(handle: &Win32Handle) -> Self{
let dc = GetDC(handle.hwnd as HWND); let dc = GetDC(handle.hwnd as HWND);
Self{
dc,
window: handle.hwnd as HWND
}
}
if dc.is_null(){
return Err(SoftBufferError::PlatformError(Some("Device Context is null".into()), None));
}
Ok(
Self {
dc,
window: handle.hwnd as HWND,
}
)
}
} }
impl GraphicsContextImpl for Win32Impl { impl GraphicsContextImpl for Win32Impl {
@ -57,9 +62,9 @@ impl GraphicsContextImpl for Win32Impl {
std::mem::transmute(buffer.as_ptr()), std::mem::transmute(buffer.as_ptr()),
std::mem::transmute(&bitmap_info), std::mem::transmute(&bitmap_info),
winapi::um::wingdi::DIB_RGB_COLORS, winapi::um::wingdi::DIB_RGB_COLORS,
winapi::um::wingdi::SRCCOPY winapi::um::wingdi::SRCCOPY,
); );
ValidateRect(self.window, std::ptr::null_mut()); ValidateRect(self.window, std::ptr::null_mut());
} }
} }

View file

@ -1,38 +1,40 @@
use crate::{GraphicsContextImpl, SoftBufferError};
use raw_window_handle::{HasRawWindowHandle, XlibHandle};
use std::os::raw::{c_char, c_uint}; use std::os::raw::{c_char, c_uint};
use raw_window_handle::XlibHandle; use x11_dl::xlib::{Display, Visual, Xlib, ZPixmap, GC};
use x11_dl::xlib::{Display, GC, Visual, Xlib, ZPixmap};
use crate::GraphicsContextImpl;
pub struct X11Impl{ pub struct X11Impl {
handle: XlibHandle, handle: XlibHandle,
lib: Xlib, lib: Xlib,
gc: GC, gc: GC,
visual: *mut Visual, visual: *mut Visual,
depth: i32 depth: i32,
} }
impl X11Impl { impl X11Impl {
pub unsafe fn new<W: HasRawWindowHandle>(handle: XlibHandle) -> Result<Self, SoftBufferError<W>> {
pub unsafe fn new(handle: XlibHandle) -> Self{ let lib = match Xlib::open() {
let lib = Xlib::open().unwrap(); Ok(lib) => lib,
Err(e) => return Err(SoftBufferError::PlatformError(Some("Failed to open Xlib".into()), Some(Box::new(e))))
};
let screen = (lib.XDefaultScreen)(handle.display as *mut Display); let screen = (lib.XDefaultScreen)(handle.display as *mut Display);
let gc = (lib.XDefaultGC)(handle.display as *mut Display, screen); let gc = (lib.XDefaultGC)(handle.display as *mut Display, screen);
let visual = (lib.XDefaultVisual)(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); let depth = (lib.XDefaultDepth)(handle.display as *mut Display, screen);
Self{ Ok(
handle, Self {
lib, handle,
gc, lib,
visual, gc,
depth visual,
} depth,
}
)
} }
} }
impl GraphicsContextImpl for X11Impl { impl GraphicsContextImpl for X11Impl {
unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
//create image //create image
let image = (self.lib.XCreateImage)( let image = (self.lib.XCreateImage)(
@ -45,13 +47,9 @@ impl GraphicsContextImpl for X11Impl {
width as u32, width as u32,
height as u32, height as u32,
32, 32,
(width*4) as i32 (width * 4) as i32,
); );
if image.is_null(){
panic!("Image is null!");
}
//push image to window //push image to window
(self.lib.XPutImage)( (self.lib.XPutImage)(
self.handle.display as *mut Display, self.handle.display as *mut Display,
@ -63,11 +61,10 @@ impl GraphicsContextImpl for X11Impl {
0, 0,
0, 0,
width as c_uint, width as c_uint,
height as c_uint height as c_uint,
); );
(*image).data = std::ptr::null_mut(); (*image).data = std::ptr::null_mut();
(self.lib.XDestroyImage)(image); (self.lib.XDestroyImage)(image);
} }
}
}