Skip to main content

Overview

The Portix OS console provides a full-featured terminal emulator with command execution, input editing, scrollback history, and a tabbed graphical interface. It operates entirely in graphical mode using the framebuffer driver. Location: ~/workspace/source/kernel/src/console/terminal/terminal.rs

Architecture

Terminal Structure
pub struct Terminal {
    pub lines:         [TermLine; 128],  // Ring buffer
    pub line_count:    usize,            // Total lines written
    pub input:         [u8; 80],         // Current input buffer
    pub input_len:     usize,
    pub cursor_vis:    bool,
    pub scroll_offset: usize,            // Lines scrolled up
    
    hist_cmds:  [[u8; 80]; 16],          // Command history
    hist_lens:  [usize; 16],
    hist_count: usize,
}

Line Storage

Terminal Line
pub struct TermLine {
    pub buf:   [u8; 92],      // 92 columns
    pub len:   usize,
    pub color: LineColor,
}

pub enum LineColor {
    Normal,   // White text
    Success,  // Green (operations succeeded)
    Warning,  // Yellow (attention)
    Error,    // Red (errors)
    Info,     // Cyan (system info)
    Prompt,   // Gold (command prompt)
    Header,   // Bright blue (section headers)
}
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:18

Ring Buffer Implementation

The terminal stores lines in a circular buffer to support unlimited scrollback:
Ring Buffer
impl Terminal {
    /// Access line by logical index (0 = oldest available)
    pub fn line_at(&self, li: usize) -> &TermLine {
        &self.lines[li % TERM_ROWS]
    }
    
    /// Calculate oldest logical index
    fn oldest_logical(&self) -> usize {
        if self.line_count <= TERM_ROWS {
            0
        } else {
            self.line_count - TERM_ROWS
        }
    }
    
    /// Visible range for rendering
    pub fn visible_range(&self, max_visible: usize) -> (usize, usize) {
        let oldest = self.oldest_logical();
        let total_available = self.line_count - oldest;
        let count = total_available.min(max_visible);
        let bottom_start = self.line_count.saturating_sub(count);
        let start = bottom_start.saturating_sub(self.scroll_offset).max(oldest);
        (start, count)
    }
}
Example: If 200 lines have been written and buffer size is 128:
  • Lines 0-71 are overwritten by lines 128-199
  • oldest_logical() returns 72
  • line_at(72) returns physical slot 72 (oldest preserved line)
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:89

Input Handling

Character Input

Typing
impl Terminal {
    pub fn type_char(&mut self, c: u8) {
        if self.input_len < INPUT_MAX - 1 && c >= 32 && c < 127 {
            self.input[self.input_len] = c;
            self.input_len += 1;
        }
    }
    
    pub fn backspace(&mut self) {
        if self.input_len > 0 {
            self.input_len -= 1;
        }
    }
}

Command Execution

Command Flow
impl Terminal {
    pub fn enter(
        &mut self,
        hw: &HardwareInfo,
        pci: &PciBus
    ) {
        // 1. Echo command line with prompt
        let mut echo = [0u8; 90];
        echo[..8].copy_from_slice(b"PORTIX> ");
        echo[8..8 + self.input_len].copy_from_slice(&self.input[..self.input_len]);
        self.write_bytes(&echo, LineColor::Prompt);
        
        // 2. Add to history
        if self.input_len > 0 {
            let slot = self.hist_count % 16;
            self.hist_cmds[slot] = self.input;
            self.hist_lens[slot] = self.input_len;
            self.hist_count += 1;
        }
        
        // 3. Parse command and arguments
        let (cmd, args) = parse_input(&self.input);
        
        // 4. Dispatch to handler
        self.dispatch(cmd, args, hw, pci);
        
        // 5. Clear input buffer
        self.clear_input();
    }
}
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:148

Command System

All commands are implemented in Spanish with English names/aliases:

Information Commands

b"neofetch" | b"fetch"     // System overview with ASCII art
b"cpu" | b"lscpu"          // CPU details and ISA extensions
b"mem" | b"lsmem"          // E820 memory map
b"disks" | b"lsblk"        // ATA storage devices
b"pci" | b"lspci"          // PCI bus enumeration
b"uname" | b"ver"          // OS version
b"uptime" | b"time"        // Uptime and PIT ticks
b"date" | b"fecha"         // Time since boot

Terminal Commands

Terminal Control
b"clear" | b"cls" | b"limpiar"  // Clear screen
b"echo" | b"print"              // Print text
b"history" | b"historial"       // Show command history

Calculation Commands

Math and Conversion
b"calc" | b"math" | b"="  // Arithmetic (+, -, *, /)
b"hex"                    // Decimal to hex
b"dec"                    // Hex to decimal
b"bin"                    // Decimal to binary
b"rgb"                    // RGB components to 0xRRGGBB
Example:
PORTIX> calc 42 * 1337
  56154

PORTIX> hex 56154
  0xDB6A

PORTIX> rgb 255 170 0
  0xFFAA00

Demo Commands

Visual Effects
b"beep"              // PC speaker beep (adjustable Hz)
b"colors" | b"palette"  // Terminal color demo
b"ascii" | b"art"    // ASCII art logo
b"banner"            // Large text banner
b"progress"          // Progress bar animation
b"matrix"            // Matrix-style scrolling text
b"scrolltest"        // Generate 50 lines for scroll testing
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:188

Scrolling System

Scroll Controls

Scroll API
impl Terminal {
    /// Maximum scroll offset for current view
    pub fn max_scroll(&self, max_visible: usize) -> usize {
        let available = self.line_count.saturating_sub(self.oldest_logical());
        available.saturating_sub(max_visible)
    }
    
    /// Scroll up (toward older lines)
    pub fn scroll_up(&mut self, lines: usize, max_visible: usize) {
        let max = self.max_scroll(max_visible);
        self.scroll_offset = (self.scroll_offset + lines).min(max);
    }
    
    /// Scroll down (toward newer lines)
    pub fn scroll_down(&mut self, lines: usize) {
        self.scroll_offset = self.scroll_offset.saturating_sub(lines);
    }
    
    /// Jump to bottom (most recent output)
    pub fn scroll_to_bottom(&mut self) {
        self.scroll_offset = 0;
    }
    
    /// Check if at bottom
    pub fn at_bottom(&self) -> bool {
        self.scroll_offset == 0
    }
}
Keyboard shortcuts:
  • Page Up: Scroll up 10 lines
  • Page Down: Scroll down 10 lines
  • Home: Jump to oldest line
  • End: Jump to newest line
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:107

Tabbed Interface

The console UI uses a 5-tab layout:
Tab Layout
const TAB_NAMES: [&str; 5] = [
    "TERMINAL",    // Shell interface
    "FILES",       // File browser
    "SYSTEM",      // Hardware info
    "TASKS",       // Process list (future)
    "SETTINGS",    // Configuration (future)
];

impl Layout {
    pub fn tab_hit(&self, mx: i32, my: i32) -> i32 {
        let x = mx as usize;
        let y = my as usize;
        
        if y < self.tab_y || y >= self.tab_y + self.tab_h {
            return -1;
        }
        
        let idx = x / self.tab_w;
        if idx < 5 { idx as i32 } else { -1 }
    }
}
Visual styling:
  • Active tab: Color::TAB_ACTIVE (0x0E2240)
  • Inactive tab: Color::TAB_INACTIVE (0x030912)
  • Gold accent bar: 4-pixel strip below header
Source: ~/workspace/source/kernel/src/graphics/driver/framebuffer.rs:206

Command Examples

System Information

PORTIX> neofetch

   ██████╗  ██████╗ ██████╗ ████████╗██╗██╗  ██╗
   ██╔══██╗██╔═══██╗██╔══██╗╚══██╔══╝██║╚██╗██╔╝
   ██████╔╝██║   ██║██████╔╝   ██║   ██║ ╚███╔╝
   ██╔═══╝ ██║   ██║██╔══██╗   ██║   ██║ ██╔██╗
   ██║     ╚██████╔╝██║  ██║   ██║   ██║██╔╝ ██╗
   ╚═╝      ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚═╝╚═╝  ╚═╝

   OS: PORTIX 0.7 x86_64
   Kernel: bare-metal (no_std)
   CPU: Intel Core i5-10400 @ 4.3 GHz
   RAM: 8192 MiB
   Disk: QEMU HARDDISK (20480 MiB)

Hardware Inspection

PORTIX> pci

  +-- BUS PCI -------------------------------------------+
  Se encontraron 12 dispositivo(s):

  [B:D.F]  VendorID:DevID  Fabricante           Clase
  -------  --------------  -------------------  ---------
  [0:00.0]  8086:1237      Intel                Host Bridge
  [0:01.0]  1234:1111      QEMU/Bochs           VGA Controller
  [0:02.0]  8086:100E      Intel                Network Controller
  [0:1F.0]  8086:7000      Intel                ISA Bridge
  [0:1F.2]  8086:7010      Intel                IDE Controller

Memory Operations

PORTIX> peek 0x9000
  [0x9000] = 0x00000000E820FFFF (3892314111)

PORTIX> hexdump 0xB8000 64
  Volcado 0xB8000 (64 bytes):
  Offset    00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F  ASCII
  B8000     50 07 4F 07 52 07 54 07  49 07 58 07 20 07 4F 07  P.O.R.T.I.X. .O.
  B8010     53 07 20 07 76 07 30 07  2E 07 37 07 00 07 00 07  S. .v.0...7.....

Calculation

PORTIX> = 0xFF * 16 + 0xAA
  4266

PORTIX> bin 4266
  0b1000010101010

Help System

PORTIX> help

  +=========================================================+
  |       PORTIX v0.7  -  Referencia de Comandos            |
  +=========================================================+

  INFORMACION DEL SISTEMA:
    neofetch      Vista general del sistema con logo ASCII
    cpu           Detalles del procesador y extensiones ISA
    mem           Mapa de memoria RAM (E820)
    disks         Dispositivos de almacenamiento ATA
    pci           Enumeracion del bus PCI
    uname / ver   Version del sistema operativo
    uptime        Tiempo en linea y ticks del PIT
    date          Fecha/hora desde el arranque

  HARDWARE Y DEPURACION:
    hexdump <dir> [bytes]  Volcado hexadecimal de memoria
    peek <dir>             Leer 8 bytes en direccion fisica
    poke <dir> <val>       Escribir byte en direccion fisica
    cpuid [hoja]           Ejecutar instruccion CPUID
    ...
Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:309

Text Rendering

The console renders text using an 8×8 bitmap font:
Text Output
impl Console {
    pub fn write_at(
        &mut self,
        s: &str,
        x: usize,
        y: usize,
        color: Color
    ) {
        let (ox, oy, om) = (self.cursor_x, self.cursor_y, self.margin_x);
        self.cursor_x = x;
        self.cursor_y = y;
        self.margin_x = x;
        self.write(s, color);
        self.cursor_x = ox;
        self.cursor_y = oy;
        self.margin_x = om;
    }
}
Font source: crate::graphics::render::font::FONT_8X8 (96 printable ASCII characters) Character dimensions: 8×8 pixels, 9-pixel horizontal spacing

Line Editing

Current implementation supports:
  • Typing: Printable ASCII (32-126)
  • Backspace: Delete last character
  • Enter: Execute command
  • Tab: (reserved for completion)
Future enhancements:
  • Cursor movement (left/right arrows)
  • Insert/delete at cursor position
  • Command-line editing (Ctrl+A, Ctrl+E, etc.)
  • Tab completion
  • Up/down arrow history navigation

History

Command History
impl Terminal {
    fn cmd_history(&mut self) {
        if self.hist_count == 0 {
            self.write_line("(sin historial de comandos)", LineColor::Normal);
            return;
        }
        
        let total = self.hist_count.min(16);
        let start = if self.hist_count > 16 {
            self.hist_count - 16
        } else {
            0
        };
        
        for i in 0..total {
            let slot = (start + i) % 16;
            let cmd = &self.hist_cmds[slot][..self.hist_lens[slot]];
            self.write_line(&format!("  {}  {}", start + i + 1, cmd), LineColor::Normal);
        }
    }
}
Storage: Last 16 commands in ring buffer Source: ~/workspace/source/kernel/src/console/terminal/terminal.rs:559

Performance

  • Line wrapping: O(1) - splits at 92 columns
  • Scrollback: O(1) - ring buffer access
  • Rendering: O(n) - n = visible lines (~30)
  • Command dispatch: O(1) - hash table lookup

See Also

Graphics

Framebuffer rendering backend

Drivers

Keyboard and mouse input

Filesystem

File commands (future)

Build docs developers (and LLMs) love