bp3d_os/shell/os/
unix.rs

1// Copyright (c) 2025, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use std::cell::Cell;
30use std::mem::MaybeUninit;
31
32/// Represents an interactive terminal.
33pub struct Terminal {
34    attrs: libc::termios,
35}
36
37impl Terminal {
38    /// Creates a new instance of an interactive terminal.
39    ///
40    /// This function automatically sets-up the current OS terminal for interactive input and resets
41    /// it back on drop automatically.
42    pub fn new() -> Self {
43        let mut attrs = MaybeUninit::<libc::termios>::uninit();
44        unsafe {
45            libc::tcgetattr(0, attrs.as_mut_ptr());
46            let mut newattrs = attrs.assume_init();
47            newattrs.c_lflag &= !(libc::ICANON | libc::ECHO);
48            libc::tcsetattr(0, libc::TCSANOW, &newattrs);
49            Terminal {
50                attrs: attrs.assume_init(),
51            }
52        }
53    }
54}
55
56impl Drop for Terminal {
57    fn drop(&mut self) {
58        unsafe {
59            libc::tcsetattr(0, libc::TCSANOW, &self.attrs);
60        }
61    }
62}
63
64/// Writes the given string immediately (unbuffered).
65pub fn write(str: &str) {
66    unsafe {
67        libc::write(1, str.as_bytes().as_ptr() as _, str.len());
68    }
69}
70
71/// Returns a tuple with respectively the maximum number of columns and rows available in the
72/// terminal.
73///
74/// This function issues a syscall each time it is invoked.
75pub fn get_window_size() -> (i32, i32) {
76    let mut sz = std::mem::MaybeUninit::<libc::winsize>::uninit();
77    unsafe {
78        libc::ioctl(1, libc::TIOCGWINSZ, sz.as_mut_ptr());
79        (sz.assume_init().ws_col as _, sz.assume_init().ws_row as _)
80    }
81}
82
83thread_local! {
84    static HEIGHT: Cell<i32> = const { Cell::new(-1) };
85}
86
87/// Returns the maximum number of rows available in the terminal.
88///
89/// This function amortizes the cost of the syscall by only issuing it once for the current thread.
90pub fn get_window_height_amortized() -> i32 {
91    if HEIGHT.get() == -1 {
92        let mut sz = std::mem::MaybeUninit::<libc::winsize>::uninit();
93        unsafe {
94            libc::ioctl(1, libc::TIOCGWINSZ, sz.as_mut_ptr());
95            HEIGHT.set(sz.assume_init().ws_row as _);
96        }
97    }
98    HEIGHT.get()
99}