dos_like/lib.rs
1//! Rust high level bindings to [`dos-like`][1],
2//! the library/framework for writing applications that look
3//! like MS-DOS programs from the 1990's.
4//!
5//! [1]: https://github.com/mattiasgustavsson/dos-like
6//!
7//! This API was designed to expose the original C API
8//! while maintaining Rust's idiomatic naming and safety guarantees.
9//! Note however, that some functions in the framework
10//! cannot be made completely memory safe
11//! without introducing runtime overhead.
12//! In any case, should you find it useful,
13//! the low level unsafe bindings are available in [`dos_like_sys`].
14//!
15//! ## Using
16//!
17//! **This crate does not function as a regular library,**
18//! because it already defines a `main` function by itself.
19//! Attempting to create your own executable with its own `main` function
20//! will result in a linker error.
21//! For the building process to work,
22//! the main source file needs the `no_main` attribute
23//! and to define an extern C function `dosmain` instead.
24//!
25//! ```no_run
26//! #![no_main]
27//!
28//! #[no_mangle]
29//! pub extern "C" fn dosmain() -> i32 {
30//! // your code here
31//!
32//! 0
33//! }
34//! ```
35//!
36//! A utility macro is available as an alternative to declaring the function:
37//!
38//! ```no_run
39//! #![no_main]
40//!
41//! dos_like::dos_main! {
42//! // your code here
43//! }
44//! ```
45//!
46//! Moreover, since the initiator is based on routines in C,
47//! this also means that panic unwinding will not work,
48//! so it is best to configure your project to abort on panic.
49//! Add this to your Cargo.toml and any other custom profile:
50//!
51//! ```toml
52//! [profile.dev]
53//! panic = "abort"
54//!
55//! [profile.release]
56//! panic = "abort"
57//! ```
58//!
59//! ## Cargo features
60//!
61//! - **`disable-screen-frame`**:
62//! when enabled, compiles `dos-like` so that
63//! the CRT screen frame around the viewport does not appear.
64//! The other CRT screen effects will remain.
65#![allow(clippy::too_many_arguments)]
66
67pub mod input;
68pub mod music;
69pub mod sound;
70pub mod video;
71
72pub use input::*;
73pub use music::*;
74pub use sound::*;
75pub use video::*;
76
77pub use dos_like_sys;
78
79/// Calls `waitvbl`, which waits for a vertical blanking signal.
80///
81/// This should usually be called once per frame.
82pub fn wait_vbl() {
83 unsafe {
84 dos_like_sys::waitvbl();
85 }
86}
87
88/// Checks whether the application should shut down.
89///
90/// # Example
91///
92/// A typical application loop inside `dosmain` would look like this:
93///
94/// ```no_run
95/// # use dos_like::shutting_down;
96/// while !shutting_down() {
97/// // your code here
98/// }
99/// ```
100pub fn shutting_down() -> bool {
101 unsafe { dos_like_sys::shuttingdown() != 0 }
102}
103
104/// General error type for file loading functions which can fail
105#[derive(Debug)]
106pub enum FileError {
107 /// Invalid file path (typically due to the presence of null bytes in the string)
108 BadFilePath,
109 /// File not found, or failed to read
110 FileNotFound,
111}
112
113impl std::fmt::Display for FileError {
114 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
115 match self {
116 FileError::BadFilePath => write!(f, "Invalid file path"),
117 FileError::FileNotFound => write!(f, "Failed to read file"),
118 }
119 }
120}
121
122/// Declares and defines the main application function.
123///
124/// This macro can be used as an alternative to declaring `dosmain` manually.
125///
126/// # Example
127///
128/// This:
129///
130/// ```no_run
131/// #![no_main]
132///
133/// dos_like::dos_main! {
134/// println!("Hello")
135/// }
136/// ```
137///
138/// Expands to this:
139///
140/// ```no_run
141/// #![no_main]
142/// # use std::os::raw::{c_char, c_int};
143///
144/// #[no_mangle]
145/// pub extern "C" fn dosmain(_argc: c_int, _argv: *const *const c_char) -> c_int {
146/// println!("Hello");
147/// 0
148/// }
149/// ```
150#[macro_export]
151macro_rules! dos_main {
152 ($($t:tt)*) => {
153 #[no_mangle]
154 pub extern "C" fn dosmain(_argc: std::os::raw::c_int, _argv: *const *const std::os::raw::c_char) -> std::os::raw::c_int {
155 $($t)*;
156 0
157 }
158 };
159}