Skip to content

stdin Module Documentation

Overview

The stdin module provides comprehensive terminal input/output control for interactive command-line applications. It enables reading user input, managing terminal modes, controlling cursor position, clearing screens, and handling terminal events. Built on libuv for async I/O, it supports both line-buffered and raw character-by-character input modes.

Import

swazi
tumia stdin

Core Concepts

Input Modes

  • Normal Mode (Line-buffered): Input is buffered until Enter is pressed. Best for command-line prompts and forms.
  • Raw Mode: Characters are read immediately as typed. Essential for interactive applications like text editors, games, and custom input handlers.

Event-Driven Model

The module uses an event listener system where you register callbacks for specific events:

  • data - Input received
  • eof - End of input (Ctrl+D / EOF)
  • sigint - Interrupt signal (Ctrl+C)

API Reference

Event Handling

stdin.on(event, callback)

Registers an event listener for stdin events.

ParameterTypeDescription
eventnenoEvent name: "data", "eof", or "sigint"
callbackkaziFunction called when event occurs
Returnsvoid-

Events:

EventTriggerCallback Arguments
"data"Input received(buffer: Buffer) - Input data
"eof"Ctrl+D or EOFNone
"sigint"Ctrl+CNone

Usage:

swazi
tumia stdin

// Normal mode - line-by-line input
stdin.on("data", input => {
    data text = input.toString("utf8")
    chapisha "You typed:", text
})

stdin.on("eof", () => {
    chapisha "End of input"
    stdin.close()
})

stdin.on("sigint", () => {
    chapisha "\nInterrupted!"
    stdin.close()
})

Raw Mode Example:

swazi
stdin.setRawMode(kweli)

stdin.on("data", input => {
    data bytes = input.toArray()
    
    kwa kila byte katika bytes {
        kama byte == 27 {  // ESC key
            chapisha "\nEscape pressed!"
            stdin.setRawMode(sikweli)
            stdin.close()
        } vinginevyo {
            chapisha `Key code: ${byte}`
        }
    }
})

Terminal Mode Control

stdin.setRawMode(enabled)

Switches between raw and normal (line-buffered) input modes.

ParameterTypeDescription
enabledboolkweli for raw mode, sikweli for normal mode
Returnsvoid-

Raw Mode Characteristics:

  • Characters read immediately (no Enter required)
  • No line editing (backspace, arrow keys send raw codes)
  • Special keys send escape sequences
  • Ctrl+C sends byte 0x03 (doesn't terminate program)

Usage:

swazi
// Enable raw mode for interactive input
stdin.setRawMode(kweli)

stdin.on("data", input => {
    data char = input.toArray()[0]
    
    chagua char {
        ikiwa 119:  // 'w'
            chapisha "Move up"
            simama
        ikiwa 115:  // 's'
            chapisha "Move down"
            simama
        ikiwa 113:  // 'q'
            stdin.setRawMode(sikweli)
            stdin.close()
            simama
    }
})

stdin.pause()

Pauses stdin reading. Buffered input is discarded by default.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
chapisha "Processing... input disabled"
stdin.pause()

// Simulate long operation
timers.setTimeout(() => {
    stdin.resume()
    chapisha "Input re-enabled"
}, 3000)

stdin.resume()

Resumes stdin reading after being paused. Re-displays prompt if set.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
stdin.resume()
stdin.prompt("> ")  // Re-display prompt

stdin.setDiscardOnPause(enabled)

Controls whether buffered input is discarded when pausing.

ParameterTypeDescription
enabledboolkweli to discard input on pause (default), sikweli to keep
Returnsvoid-

Usage:

swazi
// Keep input buffer when pausing
stdin.setDiscardOnPause(sikweli)

stdin.close()

Closes stdin, restores terminal to normal mode, and cleans up resources.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
stdin.on("eof", () => {
    chapisha "Goodbye!"
    stdin.close()
})

Prompt Display

stdin.prompt(text)

Displays a prompt message to the user. Useful for interactive CLI applications.

ParameterTypeDescription
textnenoPrompt text to display
Returnsvoid-

Usage:

swazi
stdin.prompt("Enter your name: ")

stdin.on("data", input => {
    data name = input.toString("utf8")
    chapisha `Hello, ${name}!`
    stdin.prompt("Enter command: ")
})

stdin.echo(text)

Writes text directly to stdout without newline.

ParameterTypeDescription
textneno au BufferText to output
Returnsvoid-

Usage:

swazi
stdin.echo("Loading")
timers.setInterval(() => {
    stdin.echo(".")
}, 500)

Terminal Size

stdin.getTermSize()

Retrieves current terminal dimensions.

ParameterTypeDescription
Returnsobject au null{ width: namba, height: namba } or null if unavailable

Usage:

swazi
data size = stdin.getTermSize()

kama size {
    chapisha `Terminal: ${size.width}x${size.height}`
    
    kama size.width < 80 {
        chapisha "Warning: Terminal too narrow"
    }
} vinginevyo {
    chapisha "Could not determine terminal size"
}

Cursor Control

stdin.cursorTo(x, y)

Moves cursor to absolute position (0-indexed).

ParameterTypeDescription
xnambaColumn position (0 = leftmost)
ynambaRow position (0 = top)
Returnsvoid-

Usage:

swazi
stdin.cursorTo(0, 0)  // Top-left corner
stdin.echo("Header")

stdin.cursorTo(0, 10)  // Row 10
stdin.echo("Footer")

stdin.cursorMove(dx, dy)

Moves cursor relative to current position.

ParameterTypeDescription
dxnambaHorizontal offset (positive = right, negative = left)
dynambaVertical offset (positive = down, negative = up)
Returnsvoid-

Usage:

swazi
stdin.cursorMove(5, 0)   // Move 5 columns right
stdin.cursorMove(-3, 0)  // Move 3 columns left
stdin.cursorMove(0, -2)  // Move 2 rows up

stdin.saveCursor()

Saves current cursor position.

ParameterTypeDescription
Returnsvoid-

stdin.restoreCursor()

Restores previously saved cursor position.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
stdin.saveCursor()
stdin.cursorTo(0, 20)
stdin.echo("Temporary message")
timers.setTimeout(() => {
    stdin.restoreCursor()
    stdin.echo("Back to original position")
}, 2000)

stdin.hideCursor()

Hides the terminal cursor.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
stdin.hideCursor()
// ... display UI ...
stdin.showCursor()  // Always restore on exit

stdin.showCursor()

Shows the terminal cursor.

ParameterTypeDescription
Returnsvoid-

Screen Control

stdin.clearLine(mode?)

Clears the current line.

ParameterTypeDescription
modenamba (optional)0 = right of cursor, 1 = left of cursor, 2 = entire line (default)
Returnsvoid-

Usage:

swazi
stdin.clearLine(2)  // Clear entire line
stdin.cursorTo(0, stdin.getTermSize().height - 1)
stdin.echo("Status: Ready")

stdin.clearScreen(mode?)

Clears the terminal screen.

ParameterTypeDescription
modenamba (optional)0 = below cursor, 1 = above cursor, 2 = entire screen (default)
Returnsvoid-

Usage:

swazi
stdin.clearScreen(2)  // Clear entire screen
stdin.cursorTo(0, 0)
stdin.echo("Fresh start!")

stdin.clearScreenDown()

Clears screen from cursor to bottom.

ParameterTypeDescription
Returnsvoid-

Scrolling

stdin.scrollUp(lines?)

Scrolls terminal content up by specified lines.

ParameterTypeDescription
linesnamba (optional)Number of lines to scroll (default: 1)
Returnsvoid-

stdin.scrollDown(lines?)

Scrolls terminal content down by specified lines.

ParameterTypeDescription
linesnamba (optional)Number of lines to scroll (default: 1)
Returnsvoid-

Usage:

swazi
stdin.scrollUp(3)   // Scroll up 3 lines
stdin.scrollDown(1) // Scroll down 1 line

Audio Feedback

stdin.beep()

Emits terminal bell/beep sound.

ParameterTypeDescription
Returnsvoid-

Usage:

swazi
stdin.beep()  // Alert user
chapisha "Error: Invalid input"

Complete Examples

1. Interactive Password Input

swazi
tumia stdin

data password = ""
data thabiti maxLength = 20

stdin.setRawMode(kweli)
stdin.hideCursor()
stdin.prompt("Enter password: ")

stdin.on("data", input => {
    data char = input.toArray()[0]
    
    kama char == 13 {  // Enter key
        stdin.echo("\n")
        stdin.showCursor()
        stdin.setRawMode(sikweli)
        chapisha `Password captured: ${password.idadi} characters`
        stdin.close()
        rudisha
    }
    
    kama char == 127 au char == 8 {  // Backspace/Delete
        kama password.idadi > 0 {
            password = password.slice(0, -1)
            stdin.cursorMove(-1, 0)
            stdin.echo(" ")
            stdin.cursorMove(-1, 0)
        }
        rudisha
    }
    
    kama char >= 32 na char <= 126 {  // Printable characters
        kama password.idadi < maxLength {
            password += String.fromCharCode(char)
            stdin.echo("*")
        } vinginevyo {
            stdin.beep()
        }
    }
})

stdin.on("sigint", () => {
    stdin.echo("\n^C\n")
    stdin.showCursor()
    stdin.setRawMode(sikweli)
    stdin.close()
})

2. Progress Bar

swazi
tumia stdin
tumia timers

kazi drawProgressBar percent, width {
    data filled = Math.floor(width * percent / 100)
    data empty = width - filled
    
    data bar = "[" + "=".repeat(filled) + " ".repeat(empty) + "]"
    
    stdin.cursorTo(0, 5)
    stdin.clearLine(2)
    stdin.echo(`${bar} ${percent}%`)
}

stdin.hideCursor()
stdin.clearScreen(2)
stdin.cursorTo(0, 0)
stdin.echo("Downloading file...\n")

data progress = 0

data interval = timers.setInterval(() => {
    progress += 2
    drawProgressBar(progress, 50)
    
    kama progress >= 100 {
        timers.clearInterval(interval)
        stdin.cursorTo(0, 7)
        stdin.echo("Download complete!\n")
        stdin.showCursor()
        stdin.close()
    }
}, 100)

3. Menu Selection System

swazi
tumia stdin

data options = ["New Game", "Load Game", "Settings", "Quit"]
data selected = 0

kazi drawMenu {
    stdin.clearScreen(2)
    stdin.cursorTo(0, 0)
    stdin.echo("=== GAME MENU ===\n\n")
    
    kwa(data i = 0; i < options.idadi; i++) {
        kama i == selected {
            stdin.echo(`> ${options[i]} <\n`)
        } vinginevyo {
            stdin.echo(`  ${options[i]}\n`)
        }
    }
    
    stdin.echo("\nUse arrows to navigate, Enter to select, Q to quit")
}

stdin.setRawMode(kweli)
stdin.hideCursor()
drawMenu()

stdin.on("data", input => {
    data bytes = input.toArray()
    
    // Arrow keys send escape sequences: ESC[A (up), ESC[B (down)
    kama bytes.idadi == 3 na bytes[0] == 27 na bytes[1] == 91 {
        kama bytes[2] == 65 {  // Up arrow
            selected = (selected - 1 + options.idadi) % options.idadi
            drawMenu()
        } vinginevyo kama bytes[2] == 66 {  // Down arrow
            selected = (selected + 1) % options.idadi
            drawMenu()
        }
        rudisha
    }
    
    data char = bytes[0]
    
    kama char == 13 {  // Enter
        stdin.clearScreen(2)
        stdin.cursorTo(0, 0)
        stdin.showCursor()
        stdin.setRawMode(sikweli)
        chapisha `You selected: ${options[selected]}`
        stdin.close()
    } vinginevyo kama char == 113 {  // 'q'
        stdin.clearScreen(2)
        stdin.cursorTo(0, 0)
        stdin.showCursor()
        stdin.setRawMode(sikweli)
        chapisha "Quit"
        stdin.close()
    }
})

stdin.on("sigint", () => {
    stdin.showCursor()
    stdin.setRawMode(sikweli)
    stdin.close()
})

4. Live Data Dashboard

swazi
tumia stdin
tumia timers

data stats = {
    cpu: 0,
    memory: 0,
    network: 0
}

kazi updateStats {
    stats.cpu = Math.random() * 100
    stats.memory = Math.random() * 100
    stats.network = Math.random() * 1000
}

kazi drawDashboard {
    data size = stdin.getTermSize()
    
    // Header
    stdin.cursorTo(0, 0)
    stdin.clearLine(2)
    stdin.echo("=== SYSTEM MONITOR ===")
    
    // CPU
    stdin.cursorTo(0, 2)
    stdin.clearLine(2)
    stdin.echo(`CPU:     ${stats.cpu.toFixed(1)}%`)
    
    // Memory
    stdin.cursorTo(0, 3)
    stdin.clearLine(2)
    stdin.echo(`Memory:  ${stats.memory.toFixed(1)}%`)
    
    // Network
    stdin.cursorTo(0, 4)
    stdin.clearLine(2)
    stdin.echo(`Network: ${stats.network.toFixed(0)} KB/s`)
    
    // Footer
    stdin.cursorTo(0, 6)
    stdin.clearLine(2)
    stdin.echo("Press Q to quit")
    
    // Keep cursor at bottom
    stdin.cursorTo(0, size ? size.height - 1 : 10)
}

stdin.setRawMode(kweli)
stdin.hideCursor()
stdin.clearScreen(2)

// Update every second
data interval = timers.setInterval(() => {
    updateStats()
    drawDashboard()
}, 1000)

// Initial draw
drawDashboard()

stdin.on("data", input => {
    data char = input.toArray()[0]
    
    kama char == 113 au char == 81 {  // 'q' or 'Q'
        timers.clearInterval(interval)
        stdin.clearScreen(2)
        stdin.cursorTo(0, 0)
        stdin.showCursor()
        stdin.setRawMode(sikweli)
        chapisha "Dashboard closed"
        stdin.close()
    }
})

stdin.on("sigint", () => {
    timers.clearInterval(interval)
    stdin.showCursor()
    stdin.setRawMode(sikweli)
    stdin.close()
})

5. Simple Text Editor (Line Editor)

swazi
tumia stdin

data lines = [""]
data currentLine = 0
data cursorX = 0

kazi drawEditor {
    stdin.clearScreen(2)
    stdin.cursorTo(0, 0)
    stdin.echo("=== TEXT EDITOR ===\n")
    stdin.echo("Ctrl+S: Save | Ctrl+Q: Quit\n\n")
    
    kwa(i = 0; i < lines.idadi; i++) {
        data prefix = i == currentLine ? "> " : "  "
        stdin.echo(`${prefix}${lines[i]}\n`)
    }
}

stdin.setRawMode(kweli)
drawEditor()

stdin.on("data", input => {
    data bytes = input.toArray()
    data char = bytes[0]
    
    // Ctrl+S (Save)
    kama char == 19 {
        stdin.setRawMode(sikweli)
        stdin.clearScreen(2)
        stdin.cursorTo(0, 0)
        chapisha "Saved!"
        kwa kila line katika lines {
            chapisha line
        }
        stdin.close()
        rudisha
    }
    
    // Ctrl+Q (Quit)
    kama char == 17 {
        stdin.setRawMode(sikweli)
        stdin.clearScreen(2)
        stdin.cursorTo(0, 0)
        chapisha "Quit without saving"
        stdin.close()
        rudisha
    }
    
    // Enter - new line
    kama char == 13 {
        currentLine++
        lines.splice(currentLine, 0, "")
        cursorX = 0
        drawEditor()
        rudisha
    }
    
    // Backspace
    kama char == 127 au char == 8 {
        kama cursorX > 0 {
            data line = lines[currentLine]
            lines[currentLine] = line.slice(0, cursorX - 1) + line.slice(cursorX)
            cursorX--
            drawEditor()
        }
        rudisha
    }
    
    // Printable characters
    kama char >= 32 na char <= 126 {
        data line = lines[currentLine]
        data c = String.fromCharCode(char)
        lines[currentLine] = line.slice(0, cursorX) + c + line.slice(cursorX)
        cursorX++
        drawEditor()
    }
})

stdin.on("sigint", () => {
    stdin.setRawMode(sikweli)
    stdin.clearScreen(2)
    stdin.cursorTo(0, 0)
    chapisha "Interrupted"
    stdin.close()
})

Use Cases

1. Command-Line Tools

  • Interactive prompts for user input
  • Configuration wizards
  • CLI questionnaires

2. Games

  • Terminal-based games (snake, tetris, roguelikes)
  • Real-time keyboard controls
  • Menu navigation systems

3. System Monitoring

  • Live dashboards
  • Resource monitors (CPU, memory, network)
  • Log viewers with auto-refresh

4. Text Editors

  • Simple text editors
  • Code snippet editors
  • Configuration file editors

5. Progress Indicators

  • Download/upload progress bars
  • Installation progress
  • Build/compile status displays

6. Interactive Forms

  • Password input with masking
  • Multi-step forms
  • Data entry interfaces

7. Terminal UI Applications

  • File browsers
  • Process managers
  • Database query tools

Best Practices

Always Clean Up

swazi
// Always restore terminal state
stdin.on("sigint", () => {
    stdin.showCursor()
    stdin.setRawMode(sikweli)
    stdin.close()
})

// Or use error handling
jaribu {
    // ... your code ...
} makosa err {
    stdin.showCursor()
    stdin.setRawMode(sikweli)
    stdin.close()
    throw err
}

Handle Terminal Resize

swazi
// Check size before drawing
data size = stdin.getTermSize()
kama size na size.width < 80 {
    chapisha "Terminal too narrow (min 80 columns)"
    rudisha
}

Use Raw Mode Judiciously

swazi
// Only enable when needed
stdin.setRawMode(kweli)
// ... capture input ...
stdin.setRawMode(sikweli)  // Restore immediately after

Buffer Management

swazi
// For text input, convert properly
stdin.on("data", input => {
    data text = input.toString("utf8").trim()
    // ... process text ...
})

Error Handling

All stdin operations throw SwaziError with type "RuntimeError" or "TypeError" on failure:

swazi
jaribu {
    stdin.setRawMode(kweli)
    stdin.cursorTo(100, 100)  // May fail on small terminals
} makosa err {
    chapisha err.type      // "RuntimeError"
    chapisha err.message   
    stdin.setRawMode(sikweli)
}

Platform Notes

  • ANSI Escape Sequences: All cursor/screen control uses ANSI codes, supported on Unix/Linux/macOS and modern Windows terminals
  • Signal Handling: SIGINT, SIGTERM, SIGABRT automatically restore terminal state
  • Terminal Requirements: Requires TTY-capable terminal (not file redirects)
  • Event Loop: Requires active libuv event loop (automatic in async contexts)