DEV Community

Cover image for Developing TUI Applications with x/term
Kenta Takeuchi
Kenta Takeuchi

Posted on • Originally published at bmf-tech.com

Developing TUI Applications with x/term

This article was originally published on bmf-tech.com.

Introduction

Recently, I tried creating a terminal-based typing game using Go's x/term package. In this article, I will share the features of the x/term package and insights I gained during TUI application development.

As a practical TUI application using x/term, I am developing a git client tool called ggc, so please give it a star if you like it.

What is the x/term Package

x/term is one of Go's experimental packages that provides low-level functionalities for terminal operations. It was previously golang.org/x/crypto/ssh/terminal, but now it is an independent package as golang.org/x/term.

Main Features

  1. Getting terminal size
width, height, err := term.GetSize(int(os.Stdout.Fd()))
Enter fullscreen mode Exit fullscreen mode
  1. Capturing low-level key inputs
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
    log.Fatal(err)
}
defer term.Restore(int(os.Stdin.Fd()), oldState)
Enter fullscreen mode Exit fullscreen mode
  1. Controlling echo
  2. Controlling terminal modes

Using these features, you can create interactive TUI applications with cursor position control and immediate key input detection.

Implementing the Typing Game

1. Basic Design

The core features of the typing game are as follows:

  • Displaying random English sentences
  • Immediate key input detection
  • Accuracy measurement
  • Counting the number of mistakes

2. Managing Terminal State

The most important aspect when using x/term is managing the terminal state:

// Set terminal to raw mode
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
    log.Fatal(err)
}

// Restore original state upon program exit
defer term.Restore(int(os.Stdin.Fd()), oldState)

// Signal handling
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
go func() {
    <-sigCh
    term.Restore(int(os.Stdin.Fd()), oldState)
    os.Exit(0)
}()
Enter fullscreen mode Exit fullscreen mode

3. Controlling Screen Display

Using ANSI escape sequences to control screen display:

// Hide cursor
fmt.Print("\033[?25l")
defer fmt.Print("\033[?25h") // Restore display on exit

// Clear screen
fmt.Print("\033[2J")

// Move cursor position (to x=10, y=5)
fmt.Printf("\033[%d;%dH", 5, 10)
Enter fullscreen mode Exit fullscreen mode

4. Performance Optimization

Here are some points to ensure display stability:

  • Controlling buffering
  • Optimizing screen updates
  • Asynchronous processing using goroutines

Implementation Points

1. Error Handling and Recovery

// Recovery process during panic
defer func() {
    if r := recover(); r != nil {
        term.Restore(int(os.Stdin.Fd()), oldState)
        fmt.Printf("Recovered from panic: %v\n", r)
    }
}()
Enter fullscreen mode Exit fullscreen mode

2. Cross-Platform Support

var clear string
if runtime.GOOS == "windows" {
    clear = "cls"
} else {
    clear = "clear"
}
cmd := exec.Command(clear)
cmd.Stdout = os.Stdout
cmd.Run()
Enter fullscreen mode Exit fullscreen mode

Conclusion

Developing TUI applications with x/term requires low-level control, but it offers a high degree of freedom. The typing game implementation example I created demonstrates basic patterns, but you can apply these to create various TUI applications.

Reference Links

Top comments (0)