use std::mem::{transmute, size_of, MaybeUninit};
use std::ptr;
use winapi::ctypes::{c_int, c_void};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::GetStdHandle;
pub use winapi::shared::windef::{HDC, HWND};
pub use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::ULONG;
pub use winapi::shared::windef::COLORREF; use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::STD_ERROR_HANDLE;
use winapi::um::wincon::{GetConsoleWindow, GetConsoleScreenBufferInfo, GetCurrentConsoleFont};
pub use winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO;
pub use winapi::um::wincontypes::CONSOLE_FONT_INFO;
use winapi::um::wingdi::{GetPixel, SetPixelV, StretchDIBits,
BITMAPINFOHEADER, BITMAPINFO, BI_RGB, CLR_INVALID, DIB_RGB_COLORS, GDI_ERROR, RGB, RGBQUAD, SRCCOPY};
pub use winapi::um::winnt::HANDLE;
use winapi::um::winnt::LONG;
use winapi::um::winuser::{GetDC, ReleaseDC};
pub struct DeviceContext {
pub console_window: HWND,
pub dc: HDC,
pub heh: &'static dyn Fn (&'static str, DWORD),
pub width: usize,
pub height: usize,
pub ce: HANDLE}
unsafe impl Send for DeviceContext {}
impl Drop for DeviceContext {
fn drop (&mut self) {
let rc = unsafe {ReleaseDC (self.console_window, self.dc)};
if rc != 1 {(self.heh) ("ReleaseDC(dc)", unsafe {GetLastError()})}}}
impl DeviceContext {
pub fn with_heh (heh: &'static dyn Fn (&'static str, DWORD)) -> Result<DeviceContext, String> {
let console_window = unsafe {GetConsoleWindow()};
if console_window == ptr::null_mut() {return ERR! ("!GetConsoleWindow")}
let dc = unsafe {GetDC (console_window)};
if dc == ptr::null_mut() {return ERR! ("!GetDC")}
let ce = unsafe {GetStdHandle (STD_ERROR_HANDLE)};
if ce == INVALID_HANDLE_VALUE {return ERR! ("!GetStdHandle")}
Ok (DeviceContext {
console_window,
dc,
heh,
width: 0,
height: 0,
ce})}
pub fn new() -> Result<DeviceContext, String> {
DeviceContext::with_heh (&|_label, _errno| {})}
pub fn rgba (r: u8, g: u8, b: u8) -> u32 {
let quad = RGBQUAD {
rgbBlue: b,
rgbGreen: g,
rgbRed: r,
rgbReserved: 0};
unsafe {transmute (quad)}}
pub fn stretch (&self, x: i32, y: i32, w: usize, h: usize,
sx: i32, sy: i32, sw: usize, sh: usize, rgba: &[u32]) -> Result<i32, DWORD> {
let mut inf: BITMAPINFO = unsafe {MaybeUninit::zeroed().assume_init()};
inf.bmiHeader.biSize = size_of::<BITMAPINFOHEADER>() as u32;
inf.bmiHeader.biWidth = sw as LONG;
inf.bmiHeader.biHeight = - (sh as LONG); inf.bmiHeader.biPlanes = 1;
inf.bmiHeader.biBitCount = 32;
inf.bmiHeader.biCompression = BI_RGB;
inf.bmiHeader.biSizeImage = (sw * sh * 4) as u32;
let rc = unsafe {StretchDIBits (
self.dc, x, y, w as c_int, h as c_int,
sx, sy, sw as c_int, sh as c_int, rgba.as_ptr() as *const c_void, &inf, DIB_RGB_COLORS, SRCCOPY)};
if rc == 0 || rc as ULONG == GDI_ERROR {Err (unsafe {GetLastError()})} else {Ok (rc)}}
pub fn set_pixel (&self, x: i32, y: i32, r: u8, g: u8, b: u8) -> Result<(), DWORD> {
let rc = unsafe {SetPixelV (self.dc, x, y, RGB (r, g, b))};
if rc == 0 {Err (unsafe {GetLastError()})} else {Ok(())}}
pub fn get_pixel (&self, x: i32, y: i32) -> Result<COLORREF, ()> {
let rc = unsafe {GetPixel (self.dc, x as c_int, y as c_int)};
if rc == CLR_INVALID {
Err (())
} else {
Ok (rc)}}
pub fn scan_size (&mut self) {
if let Err (()) = self.get_pixel (1, 1) { let errno = unsafe {GetLastError()};
if errno != 0 {(self.heh) ("get_pixel (1, 1)", errno)}
return}
let (mut width, mut height) = (0, 0);
while self.get_pixel (width + 31, height + 31) .is_ok() {width += 31; height += 31}
while self.get_pixel (width + 9, height) .is_ok() {width += 9}
while self.get_pixel (width + 1, height) .is_ok() {width += 1}
while self.get_pixel (width, height + 1) .is_ok() {height += 1}
self.width = width as usize + 1; self.height = height as usize + 1}
pub fn font (&self) -> Result<CONSOLE_FONT_INFO, String> {
let mut font: CONSOLE_FONT_INFO = unsafe {MaybeUninit::zeroed().assume_init()};
let rc = unsafe {GetCurrentConsoleFont (self.ce, 0, &mut font)};
if rc == 0 {ERR! ("!GetCurrentConsoleFont")} else {Ok (font)}}
pub fn screen_buffer_info (&self) -> Result<CONSOLE_SCREEN_BUFFER_INFO, DWORD> {
let mut sbi: CONSOLE_SCREEN_BUFFER_INFO = unsafe {MaybeUninit::zeroed().assume_init()};
let rc = unsafe {GetConsoleScreenBufferInfo (self.ce, &mut sbi)};
if rc == 0 {Err (unsafe {GetLastError()})} else {Ok (sbi)}}
pub fn translate (&self) -> Result<(Translation, CONSOLE_SCREEN_BUFFER_INFO), String> {
let sbi = try_s! (self.screen_buffer_info());
let (mut tw, mut th) = (1, 2);
let sw = (sbi.srWindow.Right - sbi.srWindow.Left) as i16;
if sw > 0 {while (tw + 1) as usize * sw as usize <= self.width {tw += 1}}
let sh = (sbi.srWindow.Bottom - sbi.srWindow.Top) as i16 + 1;
if sh > 0 {while (th + 1) as usize * sh as usize <= self.height {th += 1}}
Ok ((Translation {
cxa: sbi.dwCursorPosition.X as i16,
left: sbi.srWindow.Left as i16,
cya: sbi.dwCursorPosition.Y as i16,
top: sbi.srWindow.Top as i16,
sw,
sh,
tw,
th
}, sbi))}}
#[derive(Clone, Debug)]
pub struct Translation {
pub cxa: i16,
pub left: i16,
pub cya: i16,
pub top: i16,
pub sw: i16,
pub sh: i16,
pub tw: i8,
pub th: i8}
impl Translation {
pub fn cx (&self) -> i32 {
self.cxa as i32 - self.left as i32}
pub fn cy (&self) -> i32 {
self.cya as i32 - self.top as i32}}