use std::{
ffi::{CStr, CString},
num::NonZeroU32,
os::raw::{c_int, c_uint},
ptr::NonNull,
};
use crate::FileError;
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
pub enum VideoModeKind {
Text,
Graphics,
}
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
#[repr(u32)]
pub enum VideoMode {
Text40x25_8x8 = dos_like_sys::videomode_t_videomode_40x25_8x8,
Text40x25_9x16 = dos_like_sys::videomode_t_videomode_40x25_9x16,
Text80x25_8x8 = dos_like_sys::videomode_t_videomode_80x25_8x8,
Text80x25_8x16 = dos_like_sys::videomode_t_videomode_80x25_8x16,
Text80x25_9x16 = dos_like_sys::videomode_t_videomode_80x25_9x16,
Text80x43_8x8 = dos_like_sys::videomode_t_videomode_80x43_8x8,
Text80x50_8x8 = dos_like_sys::videomode_t_videomode_80x50_8x8,
Graphics320x200 = dos_like_sys::videomode_t_videomode_320x200,
Graphics320x240 = dos_like_sys::videomode_t_videomode_320x240,
Graphics320x400 = dos_like_sys::videomode_t_videomode_320x400,
Graphics640x200 = dos_like_sys::videomode_t_videomode_640x200,
Graphics640x350 = dos_like_sys::videomode_t_videomode_640x350,
Graphics640x400 = dos_like_sys::videomode_t_videomode_640x400,
Graphics640x480 = dos_like_sys::videomode_t_videomode_640x480,
}
impl VideoMode {
pub fn kind(self) -> VideoModeKind {
match self {
VideoMode::Text40x25_8x8
| VideoMode::Text40x25_9x16
| VideoMode::Text80x25_8x8
| VideoMode::Text80x25_8x16
| VideoMode::Text80x25_9x16
| VideoMode::Text80x43_8x8
| VideoMode::Text80x50_8x8 => VideoModeKind::Text,
VideoMode::Graphics320x200
| VideoMode::Graphics320x240
| VideoMode::Graphics320x400
| VideoMode::Graphics640x200
| VideoMode::Graphics640x350
| VideoMode::Graphics640x400
| VideoMode::Graphics640x480 => VideoModeKind::Graphics,
}
}
#[inline]
pub fn is_graphics(self) -> bool {
self.kind() == VideoModeKind::Graphics
}
#[inline]
pub fn is_text(self) -> bool {
self.kind() == VideoModeKind::Text
}
#[inline]
pub fn set_video_mode(self) {
set_video_mode(self)
}
}
#[inline]
pub fn set_video_mode(mode: VideoMode) {
unsafe {
dos_like_sys::setvideomode(mode as c_uint);
}
}
#[inline]
pub fn set_double_buffer(enabled: bool) {
unsafe {
dos_like_sys::setdoublebuffer(enabled as c_int);
}
}
#[inline]
pub fn screen_width() -> u16 {
unsafe { dos_like_sys::screenwidth() as u16 }
}
#[inline]
pub fn screen_height() -> u16 {
unsafe { dos_like_sys::screenheight() as u16 }
}
#[inline]
pub fn set_pal(index: usize, r: u8, g: u8, b: u8) {
unsafe {
dos_like_sys::setpal(index as c_int, r as c_int, g as c_int, b as c_int);
}
}
#[inline]
pub fn pal(index: usize) -> (u8, u8, u8) {
let (mut r, mut g, mut b) = (0, 0, 0);
unsafe {
dos_like_sys::getpal(index as c_int, &mut r, &mut g, &mut b);
(r as u8, g as u8, b as u8)
}
}
pub unsafe fn screen_buffer() -> &'static mut [u8] {
#[allow(unused_unsafe)]
unsafe {
let buf = dos_like_sys::screenbuffer();
let width = dos_like_sys::screenwidth() as usize;
let height = dos_like_sys::screenheight() as usize;
std::slice::from_raw_parts_mut(buf, width * height)
}
}
pub unsafe fn swap_buffers_and_get() -> &'static mut [u8] {
#[allow(unused_unsafe)]
unsafe {
let buf = dos_like_sys::swapbuffers();
let width = dos_like_sys::screenwidth() as usize;
let height = dos_like_sys::screenheight() as usize;
std::slice::from_raw_parts_mut(buf, width * height)
}
}
pub fn swap_buffers() {
unsafe {
dos_like_sys::swapbuffers();
}
}
pub fn blit(
x: i32,
y: i32,
source: &[u8],
width: u16,
height: u16,
src_x: u16,
src_y: u16,
src_width: u16,
src_height: u16,
) {
if width as usize * height as usize > source.len() {
panic!(
"blit: source data ({} bytes) is too short for resolution {}x{}",
source.len(),
width,
height
);
}
unsafe {
dos_like_sys::blit(
x as c_int,
y as c_int,
source.as_ptr() as *mut _,
width as c_int,
height as c_int,
src_x as c_int,
src_y as c_int,
src_width as c_int,
src_height as c_int,
);
}
}
pub fn mask_blit(
x: i32,
y: i32,
source: &[u8],
width: u16,
height: u16,
src_x: u16,
src_y: u16,
src_width: u16,
src_height: u16,
color_key: u8,
) {
if width as usize * height as usize > source.len() {
panic!(
"blit: source data ({} bytes) is too short for resolution {}x{}",
source.len(),
width,
height
);
}
unsafe {
dos_like_sys::maskblit(
x as c_int,
y as c_int,
source.as_ptr() as *mut _,
width as c_int,
height as c_int,
src_x as c_int,
src_y as c_int,
src_width as c_int,
src_height as c_int,
color_key as c_int,
);
}
}
#[inline]
pub fn clear_screen() {
unsafe {
dos_like_sys::clearscreen();
}
}
#[inline]
pub fn pixel(x: i32, y: i32) -> u8 {
unsafe { dos_like_sys::getpixel(x as c_int, y as c_int) as u8 }
}
#[inline]
pub fn put_pixel(x: u16, y: u16, color: u8) {
unsafe {
dos_like_sys::putpixel(x as c_int, y as c_int, color as c_int);
}
}
#[inline]
pub fn h_line(x: i32, y: i32, len: u16, color: u8) {
unsafe {
dos_like_sys::hline(x as c_int, y as c_int, len as c_int, color as c_int);
}
}
#[inline]
pub fn set_color(color: u8) {
unsafe {
dos_like_sys::setcolor(color as c_int);
}
}
#[inline]
pub fn get_color() -> u8 {
unsafe { dos_like_sys::getcolor() as u8 }
}
#[inline]
pub fn line(x1: i32, y1: i32, x2: i32, y2: i32) {
unsafe {
dos_like_sys::line(x1 as c_int, y1 as c_int, x2 as c_int, y2 as c_int);
}
}
#[inline]
pub fn rectangle(x1: i32, y1: i32, width: u16, height: u16) {
unsafe {
dos_like_sys::rectangle(x1 as c_int, y1 as c_int, width as c_int, height as c_int);
}
}
#[inline]
pub fn bar(x1: i32, y1: i32, width: u16, height: u16) {
unsafe {
dos_like_sys::bar(x1 as c_int, y1 as c_int, width as c_int, height as c_int);
}
}
#[inline]
pub fn circle(x: i32, y: i32, r: u16) {
unsafe {
dos_like_sys::circle(x as c_int, y as c_int, r as c_int);
}
}
#[inline]
pub fn fill_circle(x: i32, y: i32, r: u16) {
unsafe {
dos_like_sys::fillcircle(x as c_int, y as c_int, r as c_int);
}
}
#[inline]
pub fn ellipse(x: i32, y: i32, rx: u16, ry: u16) {
unsafe {
dos_like_sys::ellipse(x as c_int, y as c_int, rx as c_int, ry as c_int);
}
}
#[inline]
pub fn fill_ellipse(x: i32, y: i32, rx: u16, ry: u16) {
unsafe {
dos_like_sys::fillellipse(x as c_int, y as c_int, rx as c_int, ry as c_int);
}
}
#[inline]
pub fn draw_poly(points: &[i32]) {
assert!(!points.is_empty() && points.len() % 2 == 0);
unsafe {
dos_like_sys::drawpoly(points.as_ptr() as *mut _, (points.len() / 2) as c_int);
}
}
#[inline]
pub fn fill_poly(points: &[i32]) {
assert!(!points.is_empty() && points.len() % 2 == 0);
unsafe {
dos_like_sys::fillpoly(points.as_ptr() as *mut _, (points.len() / 2) as c_int);
}
}
pub fn flood_fill(x: i32, y: i32) {
unsafe {
dos_like_sys::floodfill(x as c_int, y as c_int);
}
}
pub fn boundary_fill(x: i32, y: i32, boundary: u8) {
unsafe {
dos_like_sys::boundaryfill(x as c_int, y as c_int, boundary as c_int);
}
}
pub fn out_text_xy(x: i32, y: i32, text: impl AsRef<[u8]>) {
let text = CString::new(text.as_ref()).unwrap();
unsafe {
dos_like_sys::outtextxy(x as c_int, y as c_int, text.as_ptr() as *const _);
}
}
pub fn wrap_text_xy(x: i32, y: i32, text: impl AsRef<[u8]>, width: u16) {
let text = CString::new(text.as_ref()).unwrap();
unsafe {
dos_like_sys::wraptextxy(
x as c_int,
y as c_int,
text.as_ptr() as *const _,
width as c_int,
);
}
}
pub fn center_text_xy(x: i32, y: i32, text: impl AsRef<[u8]>, width: u16) {
let text = CString::new(text.as_ref()).unwrap();
unsafe {
dos_like_sys::centertextxy(
x as c_int,
y as c_int,
text.as_ptr() as *const _,
width as c_int,
);
}
}
#[derive(Debug)]
pub struct Image {
palette: [u8; 768],
palette_count: u32,
width: u32,
height: u32,
data: NonNull<u8>,
}
unsafe impl Send for Image {}
unsafe impl Sync for Image {}
impl Image {
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn data(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(
self.data.as_ptr(),
self.width as usize * self.height as usize,
)
}
}
pub fn data_mut(&mut self) -> &mut [u8] {
unsafe {
std::slice::from_raw_parts_mut(
self.data.as_ptr(),
self.width as usize * self.height as usize,
)
}
}
pub fn palette_count(&self) -> u32 {
self.palette_count
}
pub fn palette(&self) -> &[u8] {
&self.palette[..self.palette_count as usize * 3]
}
pub fn palette_mut(&mut self) -> &mut [u8] {
&mut self.palette[..self.palette_count as usize]
}
pub fn raw_palette(&self) -> &[u8; 768] {
&self.palette
}
}
pub fn load_gif(path: impl AsRef<str>) -> Result<Image, FileError> {
let filename = CString::new(path.as_ref()).map_err(|_| FileError::BadFilePath)?;
let mut width = 0;
let mut height = 0;
let mut palcount = 0;
let mut palette = [0; 768];
unsafe {
let data = dos_like_sys::loadgif(
filename.as_ptr(),
&mut width,
&mut height,
&mut palcount,
palette.as_mut_ptr(),
);
if let Some(data) = NonNull::new(data) {
Ok(Image {
width: width as u32,
height: height as u32,
palette_count: palcount as u32,
palette,
data,
})
} else {
Err(FileError::FileNotFound)
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
#[repr(transparent)]
pub struct Font(NonZeroU32);
impl Font {
pub const DEFAULT_8X8: Font = Font(
unsafe { NonZeroU32::new_unchecked(dos_like_sys::DEFAULT_FONT_8X8) },
);
pub const DEFAULT_8X16: Font = Font(
unsafe { NonZeroU32::new_unchecked(dos_like_sys::DEFAULT_FONT_8X16) },
);
pub const DEFAULT_9X16: Font = Font(
unsafe { NonZeroU32::new_unchecked(dos_like_sys::DEFAULT_FONT_9X16) },
);
pub fn install_user_font(filename: impl AsRef<str>) -> Result<Self, FileError> {
install_user_font(filename)
}
#[inline]
pub fn from_id(id: u32) -> Option<Font> {
Self::from_raw_id(id as c_int)
}
#[inline]
fn from_raw_id(id: c_int) -> Option<Font> {
NonZeroU32::new(id as u32).map(Font)
}
#[inline]
fn to_id(self) -> c_int {
self.0.get() as c_int
}
}
pub fn install_user_font(filename: impl AsRef<str>) -> Result<Font, FileError> {
let filename = CString::new(filename.as_ref()).map_err(|_| FileError::BadFilePath)?;
unsafe {
let font_id = dos_like_sys::installuserfont(filename.as_ptr() as *const _);
Font::from_id(font_id as u32).ok_or(FileError::FileNotFound)
}
}
#[inline]
pub fn set_text_style(font: Font, bold: bool, italic: bool, underline: bool) {
unsafe {
dos_like_sys::settextstyle(
font.to_id(),
bold as c_int,
italic as c_int,
underline as c_int,
);
}
}
#[inline]
pub fn put_str(string: impl AsRef<str>) {
let text = CString::new(string.as_ref()).unwrap();
put_cstr(&text)
}
#[inline]
pub fn put_cstr(text: impl AsRef<CStr>) {
unsafe {
dos_like_sys::cputs(text.as_ref().as_ptr() as *const _);
}
}
#[inline]
pub fn text_color(color: u32) {
unsafe {
dos_like_sys::textcolor(color as c_int);
}
}
#[inline]
pub fn text_background(color: u8) {
unsafe {
dos_like_sys::textbackground(color as c_int);
}
}
#[inline]
pub fn goto_xy(x: u16, y: u16) {
unsafe {
dos_like_sys::gotoxy(x as c_int, y as c_int);
}
}
#[inline]
pub fn where_x() -> u16 {
unsafe { dos_like_sys::wherex().max(0) as u16 }
}
#[inline]
pub fn where_y() -> u16 {
unsafe { dos_like_sys::wherex().max(0) as u16 }
}
pub fn clr_scr() {
unsafe {
dos_like_sys::clrscr();
}
}
#[inline]
pub fn curs_on() {
unsafe {
dos_like_sys::curson();
}
}
#[inline]
pub fn curs_off() {
unsafe {
dos_like_sys::cursoff();
}
}