diff --git a/window/src/os/windows/gdi.rs b/window/src/os/windows/gdi.rs new file mode 100644 index 000000000..843fef0bc --- /dev/null +++ b/window/src/os/windows/gdi.rs @@ -0,0 +1,106 @@ +use crate::bitmaps::BitmapImage; +use failure::Fallible; +use std::io::Error as IoError; +use winapi::shared::windef::*; +use winapi::um::wingdi::*; + +pub struct GdiBitmap { + hdc: HDC, + hbitmap: HBITMAP, + data: *mut u8, + width: usize, + height: usize, +} + +impl BitmapImage for GdiBitmap { + unsafe fn pixel_data(&self) -> *const u8 { + self.data + } + + unsafe fn pixel_data_mut(&mut self) -> *mut u8 { + self.data + } + + fn image_dimensions(&self) -> (usize, usize) { + (self.width, self.height) + } +} + +impl Drop for GdiBitmap { + fn drop(&mut self) { + unsafe { + DeleteObject(self.hbitmap as _); + } + unsafe { + DeleteObject(self.hdc as _); + } + } +} + +impl GdiBitmap { + pub fn hdc(&self) -> HDC { + self.hdc + } + + pub fn hbitmap(&self) -> HBITMAP { + self.hbitmap + } + + pub fn new_compatible(width: usize, height: usize, hdc: HDC) -> Fallible { + let hdc = unsafe { CreateCompatibleDC(hdc) }; + if hdc.is_null() { + let err = IoError::last_os_error(); + failure::bail!("CreateCompatibleDC: {}", err); + } + + let mut data = std::ptr::null_mut(); + let bmi = BITMAPINFO { + bmiHeader: BITMAPINFOHEADER { + biSize: std::mem::size_of::() as u32, + biPlanes: 1, + biBitCount: 32, + biWidth: width as i32, + biHeight: height as i32, + biClrImportant: 0, + biClrUsed: 0, + biCompression: 0, + biSizeImage: width as u32 * height as u32 * 4, + biXPelsPerMeter: 0, + biYPelsPerMeter: 0, + }, + bmiColors: [RGBQUAD { + rgbBlue: 0, + rgbRed: 0, + rgbGreen: 0, + rgbReserved: 0, + }], + }; + let hbitmap = unsafe { + CreateDIBSection( + hdc, + &bmi, + DIB_RGB_COLORS, + &mut data, + std::ptr::null_mut(), + 0, + ) + }; + + if hbitmap.is_null() { + let err = IoError::last_os_error(); + failure::bail!("CreateDIBSection: {}", err); + } + + unsafe { + SelectObject(hdc, hbitmap as _); + } + + Ok(Self { + hdc, + hbitmap, + data: data as *mut u8, + width, + height, + }) + } +} diff --git a/window/src/os/windows/mod.rs b/window/src/os/windows/mod.rs index d13d56872..19b54f56f 100644 --- a/window/src/os/windows/mod.rs +++ b/window/src/os/windows/mod.rs @@ -1,3 +1,4 @@ +pub mod gdi; pub mod window; /// Convert a rust string to a windows wide string diff --git a/window/src/os/windows/window.rs b/window/src/os/windows/window.rs index f673d662e..6ff5d3b53 100644 --- a/window/src/os/windows/window.rs +++ b/window/src/os/windows/window.rs @@ -1,4 +1,6 @@ +use super::gdi::*; use super::*; +use crate::bitmaps::*; use failure::Fallible; use std::io::Error as IoError; use std::ptr::{null, null_mut}; @@ -6,6 +8,7 @@ use std::sync::{Arc, Mutex}; use winapi::shared::minwindef::*; use winapi::shared::windef::*; use winapi::um::libloaderapi::GetModuleHandleW; +use winapi::um::wingdi::*; use winapi::um::winuser::*; pub trait WindowCallbacks { @@ -30,6 +33,14 @@ pub struct Window { inner: Arc>, } +fn rect_width(r: &RECT) -> i32 { + r.right - r.left +} + +fn rect_height(r: &RECT) -> i32 { + r.bottom - r.top +} + fn adjust_client_to_window_dimensions(width: usize, height: usize) -> (i32, i32) { let mut rect = RECT { left: 0, @@ -39,9 +50,7 @@ fn adjust_client_to_window_dimensions(width: usize, height: usize) -> (i32, i32) }; unsafe { AdjustWindowRect(&mut rect, WS_POPUP | WS_SYSMENU | WS_CAPTION, 0) }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - (width, height) + (rect_width(&rect), rect_height(&rect)) } fn arc_to_pointer(arc: &Arc>) -> *const Mutex { @@ -229,10 +238,47 @@ fn enable_dark_mode(hwnd: HWND) { } } +unsafe fn wm_paint(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> Option { + if let Some(_inner) = arc_from_hwnd(hwnd) { + let dc = GetDC(hwnd); + + let mut rect = RECT { + left: 0, + bottom: 0, + right: 0, + top: 0, + }; + GetClientRect(hwnd, &mut rect); + let width = rect_width(&rect) as usize; + let height = rect_height(&rect) as usize; + + let mut bitmap = GdiBitmap::new_compatible(width, height, dc).unwrap(); + + bitmap.clear(Color::rgb(0, 0, 0)); + BitBlt( + dc, + 0, + 0, + width as i32, + height as i32, + bitmap.hdc(), + 0, + 0, + SRCCOPY, + ); + ValidateRect(hwnd, std::ptr::null()); + + Some(0) + } else { + None + } +} + unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option { match msg { - WM_NCCREATE => return wm_nccreate(hwnd, msg, wparam, lparam), - WM_NCDESTROY => return wm_ncdestroy(hwnd, msg, wparam, lparam), + WM_NCCREATE => wm_nccreate(hwnd, msg, wparam, lparam), + WM_NCDESTROY => wm_ncdestroy(hwnd, msg, wparam, lparam), + WM_PAINT => wm_paint(hwnd, msg, wparam, lparam), WM_CLOSE => { if let Some(inner) = arc_from_hwnd(hwnd) { let mut inner = inner.lock().unwrap();