From dc6fc474bea88270091cd97eda5b817ae3b3ba01 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sun, 16 Jan 2022 08:59:29 -0600 Subject: [PATCH] Added better error handling --- Cargo.toml | 1 + README.md | 28 ++++++++------- examples/winit.rs | 30 ++++++++-------- examples/winit_wrong_sized_buffer.rs | 30 ++++++++-------- src/error.rs | 17 ++++++++++ src/lib.rs | 51 +++++++++++++++------------- src/win32.rs | 37 +++++++++++--------- src/x11.rs | 47 ++++++++++++------------- 8 files changed, 136 insertions(+), 105 deletions(-) create mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index 61cb903..73b0a81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["David Johnson "] license = "MIT OR Apache-2.0" [dependencies] +thiserror = "1.0.30" raw-window-handle = "0.4.2" [target.'cfg(target_os = "linux")'.dependencies] diff --git a/README.md b/README.md index 238b5d0..97da55f 100644 --- a/README.md +++ b/README.md @@ -52,15 +52,15 @@ For now, the priority for new platforms is: Example == ```no_run +use softbuffer::GraphicsContext; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; -use softbuffer::GraphicsContext; fn main() { let event_loop = EventLoop::new(); 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| { *control_flow = ControlFlow::Wait; @@ -71,26 +71,28 @@ fn main() { let size = graphics_context.window().inner_size(); (size.width, size.height) }; - let buffer = (0..((width*height) as usize)).map(|index|{ - let y = index / (width as usize); - let x = index % (width as usize); - let red = x % 255; - let green = y % 255; - let blue = (x*y) % 255; + let buffer = (0..((width * height) as usize)) + .map(|index| { + let y = index / (width as usize); + let x = index % (width as usize); + let red = x % 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 - }).collect::>(); + color as u32 + }) + .collect::>(); graphics_context.set_buffer(&buffer, width as u16, height as u16); } Event::WindowEvent { event: WindowEvent::CloseRequested, - window_id + window_id, } if window_id == graphics_context.window().id() => { *control_flow = ControlFlow::Exit; - }, + } _ => {} } }); diff --git a/examples/winit.rs b/examples/winit.rs index 094e5c9..720a8dc 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -1,12 +1,12 @@ +use softbuffer::GraphicsContext; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; -use softbuffer::GraphicsContext; fn main() { let event_loop = EventLoop::new(); 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| { *control_flow = ControlFlow::Wait; @@ -17,27 +17,29 @@ fn main() { let size = graphics_context.window().inner_size(); (size.width, size.height) }; - let buffer = (0..((width*height) as usize)).map(|index|{ - let y = index / (width as usize); - let x = index % (width as usize); - let red = x % 255; - let green = y % 255; - let blue = (x*y) % 255; + let buffer = (0..((width * height) as usize)) + .map(|index| { + let y = index / (width as usize); + let x = index % (width as usize); + let red = x % 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 - }).collect::>(); + color as u32 + }) + .collect::>(); graphics_context.set_buffer(&buffer, width as u16, height as u16); } Event::WindowEvent { event: WindowEvent::CloseRequested, - window_id + window_id, } if window_id == graphics_context.window().id() => { *control_flow = ControlFlow::Exit; - }, + } _ => {} } }); -} \ No newline at end of file +} diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 38aadce..e92f745 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -1,7 +1,7 @@ +use softbuffer::GraphicsContext; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; -use softbuffer::GraphicsContext; const BUFFER_WIDTH: usize = 256; const BUFFER_HEIGHT: usize = 128; @@ -9,34 +9,36 @@ const BUFFER_HEIGHT: usize = 128; fn main() { let event_loop = EventLoop::new(); 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| { *control_flow = ControlFlow::Wait; match event { Event::RedrawRequested(window_id) if window_id == graphics_context.window().id() => { - let buffer = (0..((BUFFER_WIDTH*BUFFER_HEIGHT) as usize)).map(|index|{ - let y = index / (BUFFER_WIDTH as usize); - let x = index % (BUFFER_WIDTH as usize); - let red = x % 255; - let green = y % 255; - let blue = (x*y) % 255; + let buffer = (0..((BUFFER_WIDTH * BUFFER_HEIGHT) as usize)) + .map(|index| { + let y = index / (BUFFER_WIDTH as usize); + let x = index % (BUFFER_WIDTH as usize); + let red = x % 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 - }).collect::>(); + color as u32 + }) + .collect::>(); graphics_context.set_buffer(&buffer, BUFFER_WIDTH as u16, BUFFER_HEIGHT as u16); } Event::WindowEvent { event: WindowEvent::CloseRequested, - window_id + window_id, } if window_id == graphics_context.window().id() => { *control_flow = ControlFlow::Exit; - }, + } _ => {} } }); -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..30f02bf --- /dev/null +++ b/src/error.rs @@ -0,0 +1,17 @@ +use std::error::Error; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SoftBufferError { + #[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, Option>) +} diff --git a/src/lib.rs b/src/lib.rs index cf1c64c..13afed2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ #![doc = include_str!("../README.md")] -#[cfg(target_os = "linux")] -mod x11; #[cfg(target_os = "windows")] mod win32; +#[cfg(target_os = "linux")] +mod x11; + +mod error; +pub use error::SoftBufferError; 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 /// 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). -pub struct GraphicsContext{ +pub struct GraphicsContext { window: W, - graphics_context_impl: Box + graphics_context_impl: Box, } impl GraphicsContext { - /// Creates a new instance of this struct, consuming the given window. /// /// # Safety /// /// - 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> { let raw_handle = window.raw_window_handle(); - let imple = match raw_handle{ + let imple = match raw_handle { #[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")] - 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)) + RawWindowHandle::Win32(win32_handle) => Box::new(win32::Win32Impl::new(&win32_handle)?), + 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, - graphics_context_impl: imple - } + graphics_context_impl: imple, + }) } /// Gets shared access to the underlying window #[inline] - pub fn window(&self) -> &W{ + pub fn window(&self) -> &W { &self.window } /// Gets mut/exclusive access to the underlying window #[inline] - pub fn window_mut(&mut self) -> &mut W{ + pub fn window_mut(&mut self) -> &mut W { &mut self.window } @@ -75,8 +81,8 @@ impl GraphicsContext { /// G: Green channel /// B: Blue channel #[inline] - pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16){ - if (width as usize)*(height as usize) != buffer.len(){ + pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + 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."); } @@ -84,15 +90,14 @@ impl GraphicsContext { 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); } -fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str{ - match handle{ +fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str { + match handle { RawWindowHandle::Xlib(_) => "Xlib", RawWindowHandle::Win32(_) => "Win32", RawWindowHandle::WinRt(_) => "WinRt", @@ -102,6 +107,6 @@ fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str{ RawWindowHandle::AppKit(_) => "AppKit", RawWindowHandle::Orbital(_) => "Orbital", 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 } -} \ No newline at end of file +} diff --git a/src/win32.rs b/src/win32.rs index a78f7db..f2691fb 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -1,13 +1,13 @@ +use crate::{GraphicsContextImpl, SoftBufferError}; +use raw_window_handle::{HasRawWindowHandle, Win32Handle}; use std::os::raw::c_int; -use raw_window_handle::Win32Handle; -use crate::GraphicsContextImpl; -use winapi::um::wingdi::{BITMAPINFOHEADER, BI_BITFIELDS, RGBQUAD, StretchDIBits}; -use winapi::um::winuser::{ValidateRect, GetDC}; -use winapi::shared::windef::{HWND, HDC}; +use winapi::shared::windef::{HDC, HWND}; +use winapi::um::wingdi::{StretchDIBits, BITMAPINFOHEADER, BI_BITFIELDS, RGBQUAD}; +use winapi::um::winuser::{GetDC, ValidateRect}; -pub struct Win32Impl{ +pub struct Win32Impl { window: HWND, - dc: HDC + dc: HDC, } // Wrap this so we can have a proper number of bmiColors to write in @@ -19,15 +19,20 @@ struct BitmapInfo { } impl Win32Impl { - - pub unsafe fn new(handle: &Win32Handle) -> Self{ + pub unsafe fn new(handle: &Win32Handle) -> Result> { 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 { @@ -57,9 +62,9 @@ impl GraphicsContextImpl for Win32Impl { std::mem::transmute(buffer.as_ptr()), std::mem::transmute(&bitmap_info), winapi::um::wingdi::DIB_RGB_COLORS, - winapi::um::wingdi::SRCCOPY + winapi::um::wingdi::SRCCOPY, ); ValidateRect(self.window, std::ptr::null_mut()); } -} \ No newline at end of file +} diff --git a/src/x11.rs b/src/x11.rs index 855cbf1..cf371ee 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -1,38 +1,40 @@ +use crate::{GraphicsContextImpl, SoftBufferError}; +use raw_window_handle::{HasRawWindowHandle, XlibHandle}; use std::os::raw::{c_char, c_uint}; -use raw_window_handle::XlibHandle; -use x11_dl::xlib::{Display, GC, Visual, Xlib, ZPixmap}; -use crate::GraphicsContextImpl; +use x11_dl::xlib::{Display, Visual, Xlib, ZPixmap, GC}; -pub struct X11Impl{ +pub struct X11Impl { handle: XlibHandle, lib: Xlib, gc: GC, visual: *mut Visual, - depth: i32 + depth: i32, } impl X11Impl { - - pub unsafe fn new(handle: XlibHandle) -> Self{ - let lib = Xlib::open().unwrap(); + pub unsafe fn new(handle: XlibHandle) -> Result> { + let lib = match Xlib::open() { + 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 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 - } + Ok( + Self { + handle, + lib, + gc, + visual, + depth, + } + ) } - } impl GraphicsContextImpl for X11Impl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { //create image let image = (self.lib.XCreateImage)( @@ -45,13 +47,9 @@ impl GraphicsContextImpl for X11Impl { width as u32, height as u32, 32, - (width*4) as i32 + (width * 4) as i32, ); - if image.is_null(){ - panic!("Image is null!"); - } - //push image to window (self.lib.XPutImage)( self.handle.display as *mut Display, @@ -63,11 +61,10 @@ impl GraphicsContextImpl for X11Impl { 0, 0, width as c_uint, - height as c_uint + height as c_uint, ); (*image).data = std::ptr::null_mut(); (self.lib.XDestroyImage)(image); } - -} \ No newline at end of file +}